branch: master commit 092486e564415dc4bab1fe600b548cf4fd18617b Merge: 0ead54c eb0d8d9 Author: Dmitry Gutov <dgu...@yandex.ru> Commit: Dmitry Gutov <dgu...@yandex.ru>
Merge commit 'eb0d8d9e687e1364098f9abc6f9281fcbc0d3abd' from company --- packages/company/.elpaignore | 5 + packages/company/Makefile | 4 +- packages/company/NEWS.md | 19 +- packages/company/company-bbdb.el | 17 +- packages/company/company-capf.el | 9 +- packages/company/company-clang.el | 13 +- packages/company/company-cmake.el | 129 +++- packages/company/company-etags.el | 2 +- packages/company/company-files.el | 1 + packages/company/company-semantic.el | 10 + packages/company/company-tests.el | 915 +------------------- packages/company/company.el | 378 +++++---- packages/company/test/all.el | 28 + packages/company/test/async-tests.el | 161 ++++ packages/company/test/clang-tests.el | 25 + packages/company/test/core-tests.el | 481 ++++++++++ .../elisp-tests.el} | 9 +- packages/company/test/frontends-tests.el | 298 +++++++ packages/company/test/keywords-tests.el | 32 + packages/company/test/template-tests.el | 91 ++ packages/company/test/transformers-tests.el | 58 ++ 21 files changed, 1569 insertions(+), 1116 deletions(-) diff --git a/packages/company/.elpaignore b/packages/company/.elpaignore new file mode 100644 index 0000000..9f31d8a --- /dev/null +++ b/packages/company/.elpaignore @@ -0,0 +1,5 @@ +.travis.yml +.gitignore +Makefile +test/ +company-tests.el diff --git a/packages/company/Makefile b/packages/company/Makefile index c52be4b..fada444 100644 --- a/packages/company/Makefile +++ b/packages/company/Makefile @@ -20,11 +20,11 @@ clean: @rm -rf company-*/ company-*.tar company-*.tar.bz2 *.elc ert.el test: - ${EMACS} -Q -nw -L . -l company-tests.el -l company-elisp-tests.el \ + ${EMACS} -Q -nw -L . -l test/all.el \ --eval "(let (pop-up-windows) (ert t))" test-batch: - ${EMACS} -Q --batch -L . -l company-tests.el -l company-elisp-tests.el \ + ${EMACS} -Q --batch -L . -l test/all.el \ --eval "(ert-run-tests-batch-and-exit '(not (tag interactive)))" compile: diff --git a/packages/company/NEWS.md b/packages/company/NEWS.md index 56bebb6..553155e 100644 --- a/packages/company/NEWS.md +++ b/packages/company/NEWS.md @@ -1,5 +1,22 @@ # History of user-visible changes +## Next + +* Pressing `M-n` or `M-p` doesn't quit the search mode. +* New command `company-complete-common-or-cycle`. No default binding. +* `company-search-toggle-filtering` replaced `company-search-kill-others`. +* Quitting the search mode resets the filtering. +* Pressing `backspace` in the search mode deletes the character at the end of + the search string. +* `company-semantic` displays function arguments as annotations. +* New user option, `company-bbdb-modes`. +* `company-show-numbers` and `company-complete-number` now use visual numbering + of the candidates, taking into account only the ones currently displayed. +* `company-complete-number` can be bound to keypad numbers directly, with or + without modifiers. +* `company-cmake` expands `<LANG>` and `<CONFIG>` placeholders inside variable + names. + ## 2014-10-15 (0.8.6) * `company-clang` and `company-template-c-like-templatify` support templated @@ -14,7 +31,7 @@ * `company-ropemacs` is only used when `ropemacs-mode` is on. * `company-gtags` is enabled in all `prog-mode` derivatives by default. * `company-end-of-buffer-workaround` is not used anymore. -* `company-begin-commands` includes several `cc-mode` commands. +* `company-begin-commands` includes some of `cc-mode` commands. ## 2014-08-27 (0.8.3) diff --git a/packages/company/company-bbdb.el b/packages/company/company-bbdb.el index 22741a2..58be84c 100644 --- a/packages/company/company-bbdb.el +++ b/packages/company/company-bbdb.el @@ -27,6 +27,15 @@ (declare-function bbdb-dwim-mail "bbdb-com") (declare-function bbdb-search "bbdb-com") +(defgroup company-bbdb nil + "Completion back-end for BBDB." + :group 'company) + +(defcustom company-bbdb-modes '(message-mode) + "Major modes in which `company-bbdb' may complete." + :type '(repeat (symbol :tag "Major mode")) + :package-version '(company . "0.8.8")) + (defun company-bbdb--candidates (arg) (cl-mapcan (lambda (record) (mapcar (lambda (mail) (bbdb-dwim-mail record mail)) @@ -35,15 +44,15 @@ ;;;###autoload (defun company-bbdb (command &optional arg &rest ignore) - "`company-mode' completion back-end for `bbdb'." + "`company-mode' completion back-end for BBDB." (interactive (list 'interactive)) (cl-case command (interactive (company-begin-backend 'company-bbdb)) - (prefix (and (eq major-mode 'message-mode) + (prefix (and (memq major-mode company-bbdb-modes) (featurep 'bbdb-com) - (looking-back "^\\(To\\|Cc\\|Bcc\\):.*" + (looking-back "^\\(To\\|Cc\\|Bcc\\): *\\(.*\\)" (line-beginning-position)) - (company-grab-symbol))) + (match-string-no-properties 2))) (candidates (company-bbdb--candidates arg)) (sorted t) (no-cache t))) diff --git a/packages/company/company-capf.el b/packages/company/company-capf.el index b630025..4962a26 100644 --- a/packages/company/company-capf.el +++ b/packages/company/company-capf.el @@ -138,9 +138,14 @@ (`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))) + (exit-function (plist-get (nthcdr 4 res) :exit-function)) + (table (nth 3 res)) + (pred (plist-get (nthcdr 4 res) :predicate))) (if exit-function - (funcall exit-function arg 'finished)))) + ;; Follow the example of `completion--done'. + (funcall exit-function arg + (if (eq (try-completion arg table pred) t) + 'finished 'sole))))) )) (provide 'company-capf) diff --git a/packages/company/company-clang.el b/packages/company/company-clang.el index 90757a8..8114b3b 100644 --- a/packages/company/company-clang.el +++ b/packages/company/company-clang.el @@ -110,7 +110,7 @@ or automatically through a custom `company-clang-prefix-guesser'." ;; TODO: Handle Pattern (syntactic hints would be neat). ;; Do we ever see OVERLOAD (or OVERRIDE)? (defconst company-clang--completion-pattern - "^COMPLETION: \\_<\\(%s[a-zA-Z0-9_:<>]*\\)\\(?: : \\(.*\\)$\\)?$") + "^COMPLETION: \\_<\\(%s[a-zA-Z0-9_:]*\\)\\(?: : \\(.*\\)$\\)?$") (defconst company-clang--error-buffer-name "*clang-error*") @@ -150,7 +150,13 @@ or automatically through a custom `company-clang-prefix-guesser'." ((string-match "[^:]:[^:]" meta) (substring meta (1+ (match-beginning 0)))) ((string-match "\\((.*)[ a-z]*\\'\\)" meta) - (match-string 1 meta))))) + (let ((paren (match-beginning 1))) + (if (not (eq (aref meta (1- paren)) ?>)) + (match-string 1 meta) + (with-temp-buffer + (insert meta) + (goto-char paren) + (substring meta (1- (search-backward "<")))))))))) (defun company-clang--strip-formatting (text) (replace-regexp-in-string @@ -182,7 +188,8 @@ or automatically through a custom `company-clang-prefix-guesser'." (defun company-clang--start-process (prefix callback &rest args) (let ((objc (derived-mode-p 'objc-mode)) - (buf (get-buffer-create "*clang-output*"))) + (buf (get-buffer-create "*clang-output*")) + (process-adaptive-read-buffering nil)) (with-current-buffer buf (erase-buffer)) (if (get-buffer-process buf) (funcall callback nil) diff --git a/packages/company/company-cmake.el b/packages/company/company-cmake.el index a466f60..e2962f5 100644 --- a/packages/company/company-cmake.el +++ b/packages/company/company-cmake.el @@ -1,9 +1,9 @@ ;;; company-cmake.el --- company-mode completion back-end for CMake -;; Copyright (C) 2013 Free Software Foundation, Inc. +;; Copyright (C) 2013-2014 Free Software Foundation, Inc. ;; Author: Chen Bin <chenbin DOT sh AT gmail> -;; Version: 0.1 +;; Version: 0.2 ;; 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 @@ -45,48 +45,115 @@ They affect which types of symbols we get completion candidates for.") (defvar company-cmake--completion-pattern - "^\\(%s[a-zA-Z0-9_]%s\\)$" + "^\\(%s[a-zA-Z0-9_<>]%s\\)$" "Regexp to match the candidates.") (defvar company-cmake-modes '(cmake-mode) "Major modes in which cmake may complete.") +(defvar company-cmake--candidates-cache nil + "Cache for the raw candidates.") + (defvar company-cmake--meta-command-cache nil "Cache for command arguments to retrieve descriptions for the candidates.") -(defun company-cmake--parse-output (prefix cmd) - "Analyze the temp buffer and collect lines." - (goto-char (point-min)) - (let ((pattern (format company-cmake--completion-pattern +(defun company-cmake--replace-tags (rlt) + (setq rlt (replace-regexp-in-string + "\\(.*?\\(IS_GNU\\)?\\)<LANG>\\(.*\\)" + (lambda (_match) + (mapconcat 'identity + (if (match-beginning 2) + '("\\1CXX\\3" "\\1C\\3" "\\1G77\\3") + '("\\1CXX\\3" "\\1C\\3" "\\1Fortran\\3")) + "\n")) + rlt t)) + (setq rlt (replace-regexp-in-string + "\\(.*\\)<CONFIG>\\(.*\\)" + (mapconcat 'identity '("\\1DEBUG\\2" "\\1RELEASE\\2" + "\\1RELWITHDEBINFO\\2" "\\1MINSIZEREL\\2") + "\n") + rlt)) + rlt) + +(defun company-cmake--fill-candidates-cache (arg) + "Fill candidates cache if needed." + (let (rlt) + (unless company-cmake--candidates-cache + (setq company-cmake--candidates-cache (make-hash-table :test 'equal))) + + ;; If hash is empty, fill it. + (unless (gethash arg company-cmake--candidates-cache) + (with-temp-buffer + (let ((res (call-process company-cmake-executable nil t nil arg))) + (unless (zerop res) + (message "cmake executable exited with error=%d" res))) + (setq rlt (buffer-string))) + (setq rlt (company-cmake--replace-tags rlt)) + (puthash arg rlt company-cmake--candidates-cache)) + )) + +(defun company-cmake--parse (prefix content cmd) + (let ((start 0) + (pattern (format company-cmake--completion-pattern (regexp-quote prefix) (if (zerop (length prefix)) "+" "*"))) - (case-fold-search nil) - lines match) - (while (re-search-forward pattern nil t) - (setq match (match-string-no-properties 1)) - (puthash match cmd company-cmake--meta-command-cache) - (push match lines)) - lines)) + (lines (split-string content "\n")) + match + rlt) + (dolist (line lines) + (when (string-match pattern line) + (let ((match (match-string 1 line))) + (when match + (puthash match cmd company-cmake--meta-command-cache) + (push match rlt))))) + rlt)) (defun company-cmake--candidates (prefix) - (let ((res 0) - results - cmd) - (setq company-cmake--meta-command-cache (make-hash-table :test 'equal)) + (let (results + cmd-opts + str) + + (unless company-cmake--meta-command-cache + (setq company-cmake--meta-command-cache (make-hash-table :test 'equal))) + (dolist (arg company-cmake-executable-arguments) - (with-temp-buffer - (setq res (call-process company-cmake-executable nil t nil arg)) - (unless (eq 0 res) - (message "cmake executable exited with error=%d" res)) - (setq cmd (replace-regexp-in-string "-list$" "" arg) ) - (setq results (nconc results (company-cmake--parse-output prefix cmd))))) + (company-cmake--fill-candidates-cache arg) + (setq cmd-opts (replace-regexp-in-string "-list$" "" arg) ) + + (setq str (gethash arg company-cmake--candidates-cache)) + (when str + (setq results (nconc results + (company-cmake--parse prefix str cmd-opts))))) results)) -(defun company-cmake--meta (prefix) - (let ((cmd-opts (gethash prefix company-cmake--meta-command-cache)) +(defun company-cmake--unexpand-candidate (candidate) + (cond + ((string-match "^CMAKE_\\(C\\|CXX\\|Fortran\\)\\(_.*\\)$" candidate) + (setq candidate (concat "CMAKE_<LANG>" (match-string 2 candidate)))) + + ;; C flags + ((string-match "^\\(.*_\\)IS_GNU\\(C\\|CXX\\|G77\\)$" candidate) + (setq candidate (concat (match-string 1 candidate) "IS_GNU<LANG>"))) + + ;; C flags + ((string-match "^\\(.*_\\)OVERRIDE_\\(C\\|CXX\\|Fortran\\)$" candidate) + (setq candidate (concat (match-string 1 candidate) "OVERRIDE_<LANG>"))) + + ((string-match "^\\(.*\\)\\(_DEBUG\\|_RELEASE\\|_RELWITHDEBINFO\\|_MINSIZEREL\\)\\(.*\\)$" candidate) + (setq candidate (concat (match-string 1 candidate) + "_<CONFIG>" + (match-string 3 candidate))))) + candidate) + +(defun company-cmake--meta (candidate) + (let ((cmd-opts (gethash candidate company-cmake--meta-command-cache)) result) + (setq candidate (company-cmake--unexpand-candidate candidate)) + + ;; Don't cache the documentation of every candidate (command) + ;; Cache in this case will cost too much memory. (with-temp-buffer - (call-process company-cmake-executable nil t nil cmd-opts prefix) + (call-process company-cmake-executable nil t nil cmd-opts candidate) ;; Go to the third line, trim it and return the result. ;; Tested with cmake 2.8.9. (goto-char (point-min)) @@ -96,10 +163,12 @@ They affect which types of symbols we get completion candidates for.") (setq result (replace-regexp-in-string "^[ \t\n\r]+" "" result)) result))) -(defun company-cmake--doc-buffer (prefix) - (let ((cmd-opts (gethash prefix company-cmake--meta-command-cache))) +(defun company-cmake--doc-buffer (candidate) + (let ((cmd-opts (gethash candidate company-cmake--meta-command-cache))) + + (setq candidate (company-cmake--unexpand-candidate candidate)) (with-temp-buffer - (call-process company-cmake-executable nil t nil cmd-opts prefix) + (call-process company-cmake-executable nil t nil cmd-opts candidate) ;; Go to the third line, trim it and return the doc buffer. ;; Tested with cmake 2.8.9. (goto-char (point-min)) diff --git a/packages/company/company-etags.el b/packages/company/company-etags.el index c7ba608..1c01c91 100644 --- a/packages/company/company-etags.el +++ b/packages/company/company-etags.el @@ -54,7 +54,7 @@ buffer automatically." (let ((file (locate-dominating-file (or buffer-file-name default-directory) "TAGS"))) - (when file + (when (and file (file-regular-p file)) (list (expand-file-name file))))) (defun company-etags-buffer-table () diff --git a/packages/company/company-files.el b/packages/company/company-files.el index baaeaca..7cfc500 100644 --- a/packages/company/company-files.el +++ b/packages/company/company-files.el @@ -64,6 +64,7 @@ (expand-file-name dir) (nth 5 (file-attributes dir)))) (file (file-name-nondirectory prefix)) + (completion-ignore-case read-file-name-completion-ignore-case) candidates directories) (unless (company-file--keys-match-p key (car company-files--completion-cache)) (dolist (file (company-files--directory-files dir file)) diff --git a/packages/company/company-semantic.el b/packages/company/company-semantic.el index 6c020a3..a1c7d16 100644 --- a/packages/company/company-semantic.el +++ b/packages/company/company-semantic.el @@ -99,6 +99,14 @@ (push tag company-semantic--current-tags))) (delete "" (mapcar 'semantic-tag-name company-semantic--current-tags))) +(defun company-semantic-annotation (argument tags) + (let* ((tag (assoc argument tags)) + (kind (when tag (elt tag 1)))) + (cl-case kind + (function (let* ((prototype (semantic-format-tag-prototype tag nil nil)) + (par-pos (string-match "(" prototype))) + (when par-pos (substring prototype par-pos))))))) + (defun company-semantic--pre-prefix-length (prefix-length) "Sum up the length of all chained symbols before POS. Symbols are chained by \".\" or \"->\"." @@ -133,6 +141,8 @@ Symbols are chained by \".\" or \"->\"." (company-semantic-completions arg))) (meta (funcall company-semantic-metadata-function (assoc arg company-semantic--current-tags))) + (annotation (company-semantic-annotation arg + company-semantic--current-tags)) (doc-buffer (company-semantic-doc-buffer (assoc arg company-semantic--current-tags))) ;; Because "" is an empty context and doesn't return local variables. diff --git a/packages/company/company-tests.el b/packages/company/company-tests.el index 0663c71..f0d669d 100644 --- a/packages/company/company-tests.el +++ b/packages/company/company-tests.el @@ -1,8 +1,8 @@ -;;; company-tests.el --- company-mode tests -*- lexical-binding: t -*- +;;; company-tests.el --- company-mode test helpers -*- lexical-binding: t -*- ;; Copyright (C) 2011, 2013-2014 Free Software Foundation, Inc. -;; Author: Nikolaj Schumacher +;; Author: Dmitry Gutov ;; This file is part of GNU Emacs. @@ -19,880 +19,11 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: -;; - -;;; Code: - -(require 'ert) (require 'company) -(require 'company-keywords) -(require 'company-clang) (defun company--column (&optional pos) (car (company--col-row pos))) -;;; Core - -(ert-deftest company-sorted-keywords () - "Test that keywords in `company-keywords-alist' are in alphabetical order." - (dolist (pair company-keywords-alist) - (when (consp (cdr pair)) - (let ((prev (cadr pair))) - (dolist (next (cddr pair)) - (should (not (equal prev next))) - (should (string< prev next)) - (setq prev next)))))) - -(ert-deftest company-good-prefix () - (let ((company-minimum-prefix-length 5) - company-abort-manual-when-too-short - company--manual-action ;idle begin - (company-selection-changed t)) ;has no effect - (should (eq t (company--good-prefix-p "!@#$%"))) - (should (eq nil (company--good-prefix-p "abcd"))) - (should (eq nil (company--good-prefix-p 'stop))) - (should (eq t (company--good-prefix-p '("foo" . 5)))) - (should (eq nil (company--good-prefix-p '("foo" . 4)))) - (should (eq t (company--good-prefix-p '("foo" . t)))))) - -(ert-deftest company--manual-prefix-set-and-unset () - (with-temp-buffer - (insert "ab") - (company-mode) - (let (company-frontends - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("abc" "abd"))))))) - (company-manual-begin) - (should (equal "ab" company--manual-prefix)) - (company-abort) - (should (null company--manual-prefix))))) - -(ert-deftest company-abort-manual-when-too-short () - (let ((company-minimum-prefix-length 5) - (company-abort-manual-when-too-short t) - (company-selection-changed t)) ;has not effect - (let ((company--manual-action nil)) ;idle begin - (should (eq t (company--good-prefix-p "!@#$%"))) - (should (eq t (company--good-prefix-p '("foo" . 5)))) - (should (eq t (company--good-prefix-p '("foo" . t))))) - (let ((company--manual-action t) - (company--manual-prefix "abc")) ;manual begin from this prefix - (should (eq t (company--good-prefix-p "!@#$"))) - (should (eq nil (company--good-prefix-p "ab"))) - (should (eq nil (company--good-prefix-p 'stop))) - (should (eq t (company--good-prefix-p '("foo" . 4)))) - (should (eq t (company--good-prefix-p "abcd"))) - (should (eq t (company--good-prefix-p "abc"))) - (should (eq t (company--good-prefix-p '("bar" . t))))))) - -(ert-deftest company-multi-backend-with-lambdas () - (let ((company-backend - (list (lambda (command &optional arg &rest ignore) - (cl-case command - (prefix "z") - (candidates '("a" "b")))) - (lambda (command &optional arg &rest ignore) - (cl-case command - (prefix "z") - (candidates '("c" "d"))))))) - (should (equal (company-call-backend 'candidates "z") '("a" "b" "c" "d"))))) - -(ert-deftest company-multi-backend-filters-backends-by-prefix () - (let ((company-backend - (list (lambda (command &optional arg &rest ignore) - (cl-case command - (prefix (cons "z" t)) - (candidates '("a" "b")))) - (lambda (command &optional arg &rest ignore) - (cl-case command - (prefix "t") - (candidates '("c" "d")))) - (lambda (command &optional arg &rest ignore) - (cl-case command - (prefix "z") - (candidates '("e" "f"))))))) - (should (equal (company-call-backend 'candidates "z") '("a" "b" "e" "f"))))) - -(ert-deftest company-multi-backend-remembers-candidate-backend () - (let ((company-backend - (list (lambda (command &optional arg) - (cl-case command - (ignore-case nil) - (annotation "1") - (candidates '("a" "c")) - (post-completion "13"))) - (lambda (command &optional arg) - (cl-case command - (ignore-case t) - (annotation "2") - (candidates '("b" "d")) - (post-completion "42"))) - (lambda (command &optional arg) - (cl-case command - (annotation "3") - (candidates '("e")) - (post-completion "74")))))) - (let ((candidates (company-calculate-candidates nil))) - (should (equal candidates '("a" "b" "c" "d" "e"))) - (should (equal t (company-call-backend 'ignore-case))) - (should (equal "1" (company-call-backend 'annotation (nth 0 candidates)))) - (should (equal "2" (company-call-backend 'annotation (nth 1 candidates)))) - (should (equal "13" (company-call-backend 'post-completion (nth 2 candidates)))) - (should (equal "42" (company-call-backend 'post-completion (nth 3 candidates)))) - (should (equal "3" (company-call-backend 'annotation (nth 4 candidates)))) - (should (equal "74" (company-call-backend 'post-completion (nth 4 candidates))))))) - -(ert-deftest company-multi-backend-handles-keyword-with () - (let ((primo (lambda (command &optional arg) - (cl-case command - (prefix "a") - (candidates '("abb" "abc" "abd"))))) - (secundo (lambda (command &optional arg) - (cl-case command - (prefix "a") - (candidates '("acc" "acd")))))) - (let ((company-backend (list 'ignore 'ignore :with secundo))) - (should (null (company-call-backend 'prefix)))) - (let ((company-backend (list 'ignore primo :with secundo))) - (should (equal "a" (company-call-backend 'prefix))) - (should (equal '("abb" "abc" "abd" "acc" "acd") - (company-call-backend 'candidates "a")))))) - -(ert-deftest company-begin-backend-failure-doesnt-break-company-backends () - (with-temp-buffer - (insert "a") - (company-mode) - (should-error - (company-begin-backend (lambda (command &rest ignore)))) - (let (company-frontends - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix "a") - (candidates '("a" "ab" "ac"))))))) - (let (this-command) - (company-call 'complete)) - (should (eq 3 company-candidates-length))))) - -(ert-deftest company-require-match-explicit () - (with-temp-buffer - (insert "ab") - (company-mode) - (let (company-frontends - (company-require-match 'company-explicit-action-p) - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("abc" "abd"))))))) - (let (this-command) - (company-complete)) - (let ((last-command-event ?e)) - (company-call 'self-insert-command 1)) - (should (eq 2 company-candidates-length)) - (should (eq 3 (point)))))) - -(ert-deftest company-dont-require-match-when-idle () - (with-temp-buffer - (insert "ab") - (company-mode) - (let (company-frontends - (company-minimum-prefix-length 2) - (company-require-match 'company-explicit-action-p) - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("abc" "abd"))))))) - (company-idle-begin (current-buffer) (selected-window) - (buffer-chars-modified-tick) (point)) - (should (eq 2 company-candidates-length)) - (let ((last-command-event ?e)) - (company-call 'self-insert-command 1)) - (should (eq nil company-candidates-length)) - (should (eq 4 (point)))))) - -(ert-deftest company-dont-require-match-if-old-prefix-ended-and-was-a-match () - (with-temp-buffer - (insert "ab") - (company-mode) - (let (company-frontends - (company-require-match 'company-explicit-action-p) - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (company-grab-word)) - (candidates '("abc" "ab" "abd")) - (sorted t)))))) - (let (this-command) - (company-complete)) - (let ((last-command-event ?e)) - (company-call 'self-insert-command 1)) - (should (eq 3 company-candidates-length)) - (should (eq 3 (point))) - (let ((last-command-event ? )) - (company-call 'self-insert-command 1)) - (should (null company-candidates-length)) - (should (eq 4 (point)))))) - -(ert-deftest company-should-complete-whitelist () - (with-temp-buffer - (insert "ab") - (company-mode) - (let (company-frontends - company-begin-commands - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("abc" "abd"))))))) - (let ((company-continue-commands nil)) - (let (this-command) - (company-complete)) - (company-call 'backward-delete-char 1) - (should (null company-candidates-length))) - (let ((company-continue-commands '(backward-delete-char))) - (let (this-command) - (company-complete)) - (company-call 'backward-delete-char 1) - (should (eq 2 company-candidates-length)))))) - -(ert-deftest company-should-complete-blacklist () - (with-temp-buffer - (insert "ab") - (company-mode) - (let (company-frontends - company-begin-commands - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("abc" "abd"))))))) - (let ((company-continue-commands '(not backward-delete-char))) - (let (this-command) - (company-complete)) - (company-call 'backward-delete-char 1) - (should (null company-candidates-length))) - (let ((company-continue-commands '(not backward-delete-char-untabify))) - (let (this-command) - (company-complete)) - (company-call 'backward-delete-char 1) - (should (eq 2 company-candidates-length)))))) - -(ert-deftest company-auto-complete-explicit () - (with-temp-buffer - (insert "ab") - (company-mode) - (let (company-frontends - (company-auto-complete 'company-explicit-action-p) - (company-auto-complete-chars '(? )) - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("abcd" "abef"))))))) - (let (this-command) - (company-complete)) - (let ((last-command-event ? )) - (company-call 'self-insert-command 1)) - (should (string= "abcd " (buffer-string)))))) - -(ert-deftest company-no-auto-complete-when-idle () - (with-temp-buffer - (insert "ab") - (company-mode) - (let (company-frontends - (company-auto-complete 'company-explicit-action-p) - (company-auto-complete-chars '(? )) - (company-minimum-prefix-length 2) - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("abcd" "abef"))))))) - (company-idle-begin (current-buffer) (selected-window) - (buffer-chars-modified-tick) (point)) - (let ((last-command-event ? )) - (company-call 'self-insert-command 1)) - (should (string= "ab " (buffer-string)))))) - -(ert-deftest company-clears-explicit-action-when-no-matches () - (with-temp-buffer - (company-mode) - (let (company-frontends - company-backends) - (company-call 'manual-begin) ;; fails - (should (null company-candidates)) - (should (null (company-explicit-action-p)))))) - -(ert-deftest company-ignore-case-replaces-prefix () - (with-temp-buffer - (company-mode) - (let (company-frontends - company-end-of-buffer-workaround - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("abcd" "abef")) - (ignore-case t)))))) - (insert "A") - (let (this-command) - (company-complete)) - (should (string= "ab" (buffer-string))) - (delete-char -2) - (insert "A") ; hack, to keep it in one test - (company-complete-selection) - (should (string= "abcd" (buffer-string)))))) - -(ert-deftest company-ignore-case-with-keep-prefix () - (with-temp-buffer - (insert "AB") - (company-mode) - (let (company-frontends - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("abcd" "abef")) - (ignore-case 'keep-prefix)))))) - (let (this-command) - (company-complete)) - (company-complete-selection) - (should (string= "ABcd" (buffer-string)))))) - -(ert-deftest company-non-prefix-completion () - (with-temp-buffer - (insert "tc") - (company-mode) - (let (company-frontends - company-end-of-buffer-workaround - (company-backends - (list (lambda (command &optional arg) - (cl-case command - (prefix (buffer-substring (point-min) (point))) - (candidates '("tea-cup" "teal-color"))))))) - (let (this-command) - (company-complete)) - (should (string= "tc" (buffer-string))) - (company-complete-selection) - (should (string= "tea-cup" (buffer-string)))))) - -(ert-deftest company-pseudo-tooltip-does-not-get-displaced () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (save-excursion (insert " ff")) - (company-mode) - (let ((company-frontends '(company-pseudo-tooltip-frontend)) - (company-begin-commands '(self-insert-command)) - (company-backends - (list (lambda (c &optional arg) - (cl-case c (prefix "") (candidates '("a" "b" "c"))))))) - (let (this-command) - (company-call 'complete)) - (company-call 'open-line 1) - (should (eq 1 (overlay-start company-pseudo-tooltip-overlay))))))) - -(ert-deftest company-pseudo-tooltip-show () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (insert "aaaa\n bb\nccccccc\nddd") - (search-backward "bb") - (let ((col (company--column)) - (company-candidates-length 2) - (company-candidates '("123" "45")) - (company-backend 'ignore)) - (company-pseudo-tooltip-show (company--row) col 0) - (let ((ov company-pseudo-tooltip-overlay)) - ;; With margins. - (should (eq (overlay-get ov 'company-width) 5)) - ;; FIXME: Make it 2? - (should (eq (overlay-get ov 'company-height) company-tooltip-limit)) - (should (eq (overlay-get ov 'company-column) col)) - (should (string= (overlay-get ov 'company-display) - "\n 123 \nc 45 c\nddd\n"))))))) - -(ert-deftest company-pseudo-tooltip-edit-updates-width () - :tags '(interactive) - (with-temp-buffer - (set-window-buffer nil (current-buffer)) - (let ((company-candidates-length 5) - (company-candidates '("123" "45" "67" "89" "1011")) - (company-backend 'ignore) - (company-tooltip-limit 4) - (company-tooltip-offset-display 'scrollbar)) - (company-pseudo-tooltip-show (company--row) - (company--column) - 0) - (should (eq (overlay-get company-pseudo-tooltip-overlay 'company-width) - 6)) - (company-pseudo-tooltip-edit 4) - (should (eq (overlay-get company-pseudo-tooltip-overlay 'company-width) - 7))))) - -(ert-deftest company-preview-show-with-annotations () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (save-excursion (insert "\n")) - (let ((company-candidates-length 1) - (company-candidates '("123"))) - (company-preview-show-at-point (point)) - (let* ((ov company-preview-overlay) - (str (overlay-get ov 'after-string))) - (should (string= str "123")) - (should (eq (get-text-property 0 'cursor str) t))))))) - -(ert-deftest company-pseudo-tooltip-show-with-annotations () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (insert " ") - (save-excursion (insert "\n")) - (let ((company-candidates-length 2) - (company-backend (lambda (action &optional arg &rest _ignore) - (when (eq action 'annotation) - (cdr (assoc arg '(("123" . "(4)"))))))) - (company-candidates '("123" "45")) - company-tooltip-align-annotations) - (company-pseudo-tooltip-show-at-point (point) 0) - (let ((ov company-pseudo-tooltip-overlay)) - ;; With margins. - (should (eq (overlay-get ov 'company-width) 8)) - (should (string= (overlay-get ov 'company-display) - "\n 123(4) \n 45 \n"))))))) - -(ert-deftest company-pseudo-tooltip-show-with-annotations-right-aligned () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (insert " ") - (save-excursion (insert "\n")) - (let ((company-candidates-length 3) - (company-backend (lambda (action &optional arg &rest _ignore) - (when (eq action 'annotation) - (cdr (assoc arg '(("123" . "(4)") - ("67" . "(891011)"))))))) - (company-candidates '("123" "45" "67")) - (company-tooltip-align-annotations t)) - (company-pseudo-tooltip-show-at-point (point) 0) - (let ((ov company-pseudo-tooltip-overlay)) - ;; With margins. - (should (eq (overlay-get ov 'company-width) 13)) - (should (string= (overlay-get ov 'company-display) - "\n 123 (4) \n 45 \n 67 (891011) \n"))))))) - -(ert-deftest company-create-lines-shows-numbers () - (let ((company-show-numbers t) - (company-candidates '("x" "y" "z")) - (company-candidates-length 3) - (company-backend 'ignore)) - (should (equal '(" x 1 " " y 2 " " z 3 ") - (company--create-lines 0 999))))) - -(ert-deftest company-create-lines-truncates-annotations () - (let* ((ww (company--window-width)) - (data `(("1" . "(123)") - ("2" . nil) - ("3" . ,(concat "(" (make-string (- ww 2) ?4) ")")) - (,(make-string ww ?4) . "<4>"))) - (company-candidates (mapcar #'car data)) - (company-candidates-length 4) - (company-tooltip-margin 1) - (company-backend (lambda (cmd &optional arg) - (when (eq cmd 'annotation) - (cdr (assoc arg data))))) - company-tooltip-align-annotations) - (should (equal (list (format " 1(123)%s " (company-space-string (- ww 8))) - (format " 2%s " (company-space-string (- ww 3))) - (format " 3(444%s " (make-string (- ww 7) ?4)) - (format " %s " (make-string (- ww 2) ?4))) - (company--create-lines 0 999))) - (let ((company-tooltip-align-annotations t)) - (should (equal (list (format " 1%s(123) " (company-space-string (- ww 8))) - (format " 2%s " (company-space-string (- ww 3))) - (format " 3 (444%s " (make-string (- ww 8) ?4)) - (format " %s " (make-string (- ww 2) ?4))) - (company--create-lines 0 999)))))) - -(ert-deftest company-create-lines-truncates-common-part () - (let* ((ww (company--window-width)) - (company-candidates-length 2) - (company-tooltip-margin 1) - (company-backend #'ignore)) - (let* ((company-common (make-string (- ww 3) ?1)) - (company-candidates `(,(concat company-common "2") - ,(concat company-common "3")))) - (should (equal (list (format " %s2 " (make-string (- ww 3) ?1)) - (format " %s3 " (make-string (- ww 3) ?1))) - (company--create-lines 0 999)))) - (let* ((company-common (make-string (- ww 2) ?1)) - (company-candidates `(,(concat company-common "2") - ,(concat company-common "3")))) - (should (equal (list (format " %s " company-common) - (format " %s " company-common)) - (company--create-lines 0 999)))) - (let* ((company-common (make-string ww ?1)) - (company-candidates `(,(concat company-common "2") - ,(concat company-common "3"))) - (res (company--create-lines 0 999))) - (should (equal (list (format " %s " (make-string (- ww 2) ?1)) - (format " %s " (make-string (- ww 2) ?1))) - res)) - (should (eq 'company-tooltip-common-selection - (get-text-property (- ww 2) 'face - (car res)))) - (should (eq 'company-tooltip-selection - (get-text-property (1- ww) 'face - (car res)))) - ))) - -(ert-deftest company-create-lines-clears-out-non-printables () - :tags '(interactive) - (let (company-show-numbers - (company-candidates (list - (decode-coding-string "avalis\351e" 'utf-8) - "avatar")) - (company-candidates-length 2) - (company-backend 'ignore)) - (should (equal '(" avalis‗e " - " avatar ") - (company--create-lines 0 999))))) - -(ert-deftest company-create-lines-handles-multiple-width () - :tags '(interactive) - (let (company-show-numbers - (company-candidates '("蛙蛙蛙蛙" "蛙abc")) - (company-candidates-length 2) - (company-backend 'ignore)) - (should (equal '(" 蛙蛙蛙蛙 " - " 蛙abc ") - (company--create-lines 0 999))))) - -(ert-deftest company-column-with-composition () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (insert "lambda ()") - (compose-region 1 (1+ (length "lambda")) "\\") - (should (= (company--column) 4))))) - -(ert-deftest company-column-with-line-prefix () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (insert "foo") - (put-text-property (point-min) (point) 'line-prefix " ") - (should (= (company--column) 5))))) - -(ert-deftest company-column-with-line-prefix-on-empty-line () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (insert "\n") - (forward-char -1) - (put-text-property (point-min) (point-max) 'line-prefix " ") - (should (= (company--column) 2))))) - -(ert-deftest company-column-with-tabs () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (insert "|\t|\t|\t(") - (let ((tab-width 8)) - (should (= (company--column) 25)))))) - -(ert-deftest company-row-with-header-line-format () - :tags '(interactive) - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (should (= (company--row) 0)) - (setq header-line-format "aaaaaaa") - (should (= (company--row) 0))))) - -(ert-deftest company-plainify () - (let ((tab-width 8)) - (should (equal-including-properties - (company-plainify "\tabc\td\t") - (concat " " - "abc " - "d ")))) - (should (equal-including-properties - (company-plainify (propertize "foobar" 'line-prefix "-*-")) - "-*-foobar"))) - -(ert-deftest company-buffer-lines-with-lines-folded () - :tags '(interactive) - (with-temp-buffer - (insert (propertize "aaa\nbbb\nccc\nddd\n" 'display "aaa+\n")) - (insert "eee\nfff\nggg") - (should (equal (company-buffer-lines (point-min) (point-max)) - '("aaa" "eee" "fff" "ggg"))))) - -(ert-deftest company-buffer-lines-with-multiline-display () - :tags '(interactive) - (with-temp-buffer - (insert (propertize "a" 'display "bbb\nccc\ndddd\n")) - (insert "eee\nfff\nggg") - (should (equal (company-buffer-lines (point-min) (point-max)) - '("" "" "" "eee" "fff" "ggg"))))) - -(ert-deftest company-modify-line () - (let ((str "-*-foobar")) - (should (equal-including-properties - (company-modify-line str "zz" 4) - "-*-fzzbar")) - (should (equal-including-properties - (company-modify-line str "xx" 0) - "xx-foobar")) - (should (equal-including-properties - (company-modify-line str "zz" 10) - "-*-foobar zz")))) - -(ert-deftest company-scrollbar-bounds () - (should (equal nil (company--scrollbar-bounds 0 3 3))) - (should (equal nil (company--scrollbar-bounds 0 4 3))) - (should (equal '(0 . 0) (company--scrollbar-bounds 0 1 2))) - (should (equal '(1 . 1) (company--scrollbar-bounds 2 2 4))) - (should (equal '(2 . 3) (company--scrollbar-bounds 7 4 12))) - (should (equal '(1 . 2) (company--scrollbar-bounds 3 4 12))) - (should (equal '(1 . 3) (company--scrollbar-bounds 4 5 11)))) - -;;; Async - -(defun company-async-backend (command &optional arg) - (pcase command - (`prefix "foo") - (`candidates - (cons :async - (lambda (cb) - (run-with-timer 0.05 nil - #'funcall cb '("abc" "abd"))))))) - -(ert-deftest company-call-backend-forces-sync () - (let ((company-backend 'company-async-backend) - (company-async-timeout 0.1)) - (should (equal '("abc" "abd") (company-call-backend 'candidates))))) - -(ert-deftest company-call-backend-errors-on-timeout () - (with-temp-buffer - (let* ((company-backend (lambda (command &optional _arg) - (pcase command - (`candidates (cons :async 'ignore))))) - (company-async-timeout 0.1) - (err (should-error (company-call-backend 'candidates "foo")))) - (should (string-match-p "async timeout" (cadr err)))))) - -(ert-deftest company-call-backend-raw-passes-return-value-verbatim () - (let ((company-backend 'company-async-backend)) - (should (equal "foo" (company-call-backend-raw 'prefix))) - (should (equal :async (car (company-call-backend-raw 'candidates "foo")))) - (should (equal 'closure (cadr (company-call-backend-raw 'candidates "foo")))))) - -(ert-deftest company-manual-begin-forces-async-candidates-to-sync () - (with-temp-buffer - (company-mode) - (let (company-frontends - company-transformers - (company-backends (list 'company-async-backend))) - (company-manual-begin) - (should (equal "foo" company-prefix)) - (should (equal '("abc" "abd") company-candidates))))) - -(ert-deftest company-idle-begin-allows-async-candidates () - (with-temp-buffer - (company-mode) - (let (company-frontends - company-transformers - (company-backends (list 'company-async-backend))) - (company-idle-begin (current-buffer) (selected-window) - (buffer-chars-modified-tick) (point)) - (should (null company-candidates)) - (sleep-for 0.1) - (should (equal "foo" company-prefix)) - (should (equal '("abc" "abd") company-candidates))))) - -(ert-deftest company-idle-begin-cancels-async-candidates-if-buffer-changed () - (with-temp-buffer - (company-mode) - (let (company-frontends - (company-backends (list 'company-async-backend))) - (company-idle-begin (current-buffer) (selected-window) - (buffer-chars-modified-tick) (point)) - (should (null company-candidates)) - (insert "a") - (sleep-for 0.1) - (should (null company-candidates))))) - -(ert-deftest company-idle-begin-async-allows-immediate-callbacks () - (with-temp-buffer - (company-mode) - (let (company-frontends - (company-backends - (list (lambda (command &optional arg) - (pcase command - (`prefix (buffer-substring (point-min) (point))) - (`candidates - (let ((c (all-completions arg '("abc" "def")))) - (cons :async - (lambda (cb) (funcall cb c))))) - (`no-cache t))))) - (company-minimum-prefix-length 0)) - (company-idle-begin (current-buffer) (selected-window) - (buffer-chars-modified-tick) (point)) - (should (equal '("abc" "def") company-candidates)) - (let ((last-command-event ?a)) - (company-call 'self-insert-command 1)) - (should (equal '("abc") company-candidates))))) - -(ert-deftest company-multi-backend-forces-prefix-to-sync () - (with-temp-buffer - (let ((company-backend (list 'ignore - (lambda (command) - (should (eq command 'prefix)) - (cons :async - (lambda (cb) - (run-with-timer - 0.01 nil - (lambda () (funcall cb nil)))))) - (lambda (command) - (should (eq command 'prefix)) - "foo")))) - (should (equal "foo" (company-call-backend-raw 'prefix)))) - (let ((company-backend (list (lambda (_command) - (cons :async - (lambda (cb) - (run-with-timer - 0.01 nil - (lambda () (funcall cb "bar")))))) - (lambda (_command) - "foo")))) - (should (equal "bar" (company-call-backend-raw 'prefix)))))) - -(ert-deftest company-multi-backend-merges-deferred-candidates () - (with-temp-buffer - (let* ((immediate (lambda (command &optional arg) - (pcase command - (`prefix "foo") - (`candidates - (cons :async - (lambda (cb) (funcall cb '("f")))))))) - (company-backend (list 'ignore - (lambda (command &optional arg) - (pcase command - (`prefix "foo") - (`candidates - (should (equal arg "foo")) - (cons :async - (lambda (cb) - (run-with-timer - 0.01 nil - (lambda () (funcall cb '("a" "b"))))))))) - (lambda (command &optional arg) - (pcase command - (`prefix "foo") - (`candidates '("c" "d" "e")))) - immediate))) - (should (equal :async (car (company-call-backend-raw 'candidates "foo")))) - (should (equal '("a" "b" "c" "d" "e" "f") - (company-call-backend 'candidates "foo"))) - (let ((company-backend (list immediate))) - (should (equal '("f") (company-call-backend 'candidates "foo"))))))) - -;;; Transformers - -(ert-deftest company-occurrence-prefer-closest-above () - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (insert "foo0 -foo1 -") - (save-excursion - (insert " -foo3 -foo2")) - (let ((company-backend 'company-dabbrev) - (company-occurrence-weight-function - 'company-occurrence-prefer-closest-above)) - (should (equal '("foo1" "foo0" "foo3" "foo2" "foo4") - (company-sort-by-occurrence - '("foo0" "foo1" "foo2" "foo3" "foo4")))))))) - -(ert-deftest company-occurrence-prefer-any-closest () - (with-temp-buffer - (save-window-excursion - (set-window-buffer nil (current-buffer)) - (insert "foo0 -foo1 -") - (save-excursion - (insert " -foo3 -foo2")) - (let ((company-backend 'company-dabbrev) - (company-occurrence-weight-function - 'company-occurrence-prefer-any-closest)) - (should (equal '("foo1" "foo3" "foo0" "foo2" "foo4") - (company-sort-by-occurrence - '("foo0" "foo1" "foo2" "foo3" "foo4")))))))) - -;;; Template - -(ert-deftest company-template-removed-after-the-last-jump () - (with-temp-buffer - (insert "{ }") - (goto-char 2) - (let ((tpl (company-template-declare-template (point) (1- (point-max))))) - (save-excursion - (dotimes (i 2) - (insert " ") - (company-template-add-field tpl (point) "foo"))) - (company-call 'template-forward-field) - (should (= 3 (point))) - (company-call 'template-forward-field) - (should (= 7 (point))) - (company-call 'template-forward-field) - (should (= 11 (point))) - (should (zerop (length (overlay-get tpl 'company-template-fields)))) - (should (null (overlay-buffer tpl)))))) - -(ert-deftest company-template-removed-after-input-and-jump () - (with-temp-buffer - (insert "{ }") - (goto-char 2) - (let ((tpl (company-template-declare-template (point) (1- (point-max))))) - (save-excursion - (insert " ") - (company-template-add-field tpl (point) "bar")) - (company-call 'template-move-to-first tpl) - (should (= 3 (point))) - (dolist (c (string-to-list "tee")) - (let ((last-command-event c)) - (company-call 'self-insert-command 1))) - (should (string= "{ tee }" (buffer-string))) - (should (overlay-buffer tpl)) - (company-call 'template-forward-field) - (should (= 7 (point))) - (should (null (overlay-buffer tpl)))))) - (defun company-call (name &rest args) (let* ((maybe (intern (format "company-%s" name))) (command (if (fboundp maybe) maybe name))) @@ -902,44 +33,4 @@ foo2")) (let ((this-command command)) (run-hooks 'post-command-hook)))) -(ert-deftest company-template-c-like-templatify () - (with-temp-buffer - (let ((text "foo(int a, short b)")) - (insert text) - (company-template-c-like-templatify text) - (should (equal "foo(arg0, arg1)" (buffer-string))) - (should (looking-at "arg0")) - (should (equal "int a" - (overlay-get (company-template-field-at) 'display)))))) - -(ert-deftest company-template-c-like-templatify-trims-after-closing-paren () - (with-temp-buffer - (let ((text "foo(int a, short b)!@ #1334 a")) - (insert text) - (company-template-c-like-templatify text) - (should (equal "foo(arg0, arg1)" (buffer-string))) - (should (looking-at "arg0"))))) - -(ert-deftest company-template-c-like-templatify-generics () - (with-temp-buffer - (let ((text "foo<TKey, TValue>(int i, Dict<TKey, TValue>, long l)")) - (insert text) - (company-template-c-like-templatify text) - (should (equal "foo<arg0, arg1>(arg2, arg3, arg4)" (buffer-string))) - (should (looking-at "arg0")) - (should (equal "TKey" (overlay-get (company-template-field-at) 'display))) - (search-forward "arg3") - (forward-char -1) - (should (equal "Dict<TKey, TValue>" - (overlay-get (company-template-field-at) 'display)))))) - -;;; Clang - -(ert-deftest company-clang-objc-templatify () - (with-temp-buffer - (let ((text "createBookWithTitle:andAuthor:")) - (insert text) - (company-clang-objc-templatify text) - (should (equal "createBookWithTitle:arg0 andAuthor:arg1" (buffer-string))) - (should (looking-at "arg0")) - (should (null (overlay-get (company-template-field-at) 'display)))))) +(provide 'company-tests) diff --git a/packages/company/company.el b/packages/company/company.el index cf88ddd..9658006 100644 --- a/packages/company/company.el +++ b/packages/company/company.el @@ -1,11 +1,11 @@ ;;; company.el --- Modular text completion framework -*- lexical-binding: t -*- -;; Copyright (C) 2009-2014 Free Software Foundation, Inc. +;; Copyright (C) 2009-2015 Free Software Foundation, Inc. ;; Author: Nikolaj Schumacher ;; Maintainer: Dmitry Gutov <dgu...@yandex.ru> ;; URL: http://company-mode.github.io/ -;; Version: 0.8.7 +;; Version: 0.8.8 ;; Keywords: abbrev, convenience, matching ;; Package-Requires: ((emacs "24.1") (cl-lib "0.5")) @@ -340,20 +340,20 @@ 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). 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. +text immediately before point. Returning nil from this command 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 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'. +feature must disable cache (return t to `no-cache') and might also want to +respond to `match'. Optional commands: @@ -384,10 +384,10 @@ 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. 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. +`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. `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 @@ -449,9 +449,11 @@ even if the back-end uses the asynchronous calling convention." (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." + "Functions to change the list of candidates received from backends. + +Each function gets called with the return value of the previous one. +The first one gets passed the list of candidates, already sorted and +without duplicates." :type '(choice (const :tag "None" nil) (const :tag "Sort by occurrence" (company-sort-by-occurrence)) @@ -767,10 +769,10 @@ means that `company-mode' is always turned on except in `message-mode' buffers." (interactive) (setq this-command last-command)) -(global-set-key '[31415926] 'company-ignore) +(global-set-key '[company-dummy-event] 'company-ignore) (defun company-input-noop () - (push 31415926 unread-command-events)) + (push 'company-dummy-event unread-command-events)) (defun company--posn-col-row (posn) (let ((col (car (posn-col-row posn))) @@ -978,8 +980,9 @@ Controlled by `company-auto-complete'.") ;; 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))) + (unless (equal company-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. @@ -1054,13 +1057,6 @@ can retrieve meta-data for them." (symbol-name backend)))) (setq company-lighter (format " company-<%s>" name))))))) -(defun company-apply-predicate (candidates predicate) - (let (new) - (dolist (c candidates) - (when (funcall predicate c) - (push c new))) - (nreverse new))) - (defun company-update-candidates (candidates) (setq company-candidates-length (length candidates)) (if (> company-selection 0) @@ -1077,21 +1073,17 @@ can retrieve meta-data for them." company-selection))))) (setq company-selection 0 company-candidates candidates)) - ;; Save in cache: - (push (cons company-prefix company-candidates) company-candidates-cache) ;; Calculate common. (let ((completion-ignore-case (company-call-backend 'ignore-case))) ;; 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'. + ;; `company-complete-common', unless there's only one candidate. (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 + (let ((common (try-completion "" company-candidates))) + (when (string-prefix-p company-prefix common + completion-ignore-case) common)) (car company-candidates))))) @@ -1108,11 +1100,14 @@ can retrieve meta-data for them." company-candidates-cache))) (setq candidates (all-completions prefix prev)) (cl-return t))))) - ;; no cache match, call back-end - (setq candidates - (company--process-candidates - (company--fetch-candidates prefix)))) - (setq candidates (company--transform-candidates candidates)) + (progn + ;; No cache match, call the backend. + (setq candidates (company--preprocess-candidates + (company--fetch-candidates prefix))) + ;; Save in cache. + (push (cons prefix candidates) company-candidates-cache))) + ;; Only now apply the predicate and transformers. + (setq candidates (company--postprocess-candidates candidates)) (when candidates (if (or (cdr candidates) (not (eq t (compare-strings (car candidates) nil nil @@ -1137,13 +1132,13 @@ can retrieve meta-data for them." (cdr c) (lambda (candidates) (if (not (and candidates (eq res 'done))) - ;; Fetcher called us back right away. + ;; There's no completions to display, + ;; or the fetcher called us back right away. (setq res candidates) (setq company-backend backend company-candidates-cache (list (cons prefix - (company--process-candidates - candidates)))) + (company--preprocess-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. @@ -1151,33 +1146,40 @@ can retrieve meta-data for them." (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))) +(defun company--preprocess-candidates (candidates) (unless (company-call-backend 'sorted) (setq candidates (sort candidates 'string<))) (when (company-call-backend 'duplicates) (company--strip-duplicates candidates)) candidates) +(defun company--postprocess-candidates (candidates) + (when (or company-candidates-predicate company-transformers) + (setq candidates (copy-sequence candidates))) + (when company-candidates-predicate + (setq candidates (cl-delete-if-not company-candidates-predicate candidates))) + (company--transform-candidates candidates)) + (defun company--strip-duplicates (candidates) - (let ((c2 candidates)) + (let ((c2 candidates) + (annos 'unk)) (while c2 (setcdr c2 - (let ((str (car c2)) - (anno 'unk)) - (pop c2) + (let ((str (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)))) + (progn + (setq annos 'unk) + nil) + (when (eq annos 'unk) + (setq annos (list (company-call-backend + 'annotation str)))) + (let ((anno2 (company-call-backend + 'annotation str2))) + (if (member anno2 annos) + t + (push anno2 annos) + nil)))) (pop c2)) c2))))) @@ -1288,15 +1290,14 @@ from the rest of the back-ends in the group, if any, will be left at the end." (not company-candidates) (let ((company-idle-delay 'now)) (condition-case-unless-debug err - (company--perform) + (progn + (company--perform) + ;; Return non-nil if active. + company-candidates) (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) + (quit (company-cancel)))))) (defun company-manual-begin () (interactive) @@ -1304,7 +1305,8 @@ from the rest of the back-ends in the group, if any, will be left at the end." (setq company--manual-action t) (unwind-protect (let ((company-minimum-prefix-length 0)) - (company-auto-begin)) + (or company-candidates + (company-auto-begin))) (unless company-candidates (setq company--manual-action nil)))) @@ -1366,6 +1368,7 @@ from the rest of the back-ends in the group, if any, will be left at the end." ((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. + (not (stringp new-prefix)) (<= (length new-prefix) (length company-prefix))) (member company-prefix company-candidates)) ;; Last input was a success, @@ -1455,7 +1458,8 @@ from the rest of the back-ends in the group, if any, will be left at the end." (defun company--perform () (or (and company-candidates (company--continue)) (and (company--should-complete) (company--begin-new))) - (when company-candidates + (if (not company-candidates) + (setq company-backend nil) (setq company-point (point) company--point-max (point-max)) (company-ensure-emulation-alist) @@ -1565,15 +1569,16 @@ from the rest of the back-ends in the group, if any, will be left at the end." ;;; search ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar-local company-search-string nil) +(defvar-local company-search-string "") (defvar-local company-search-lighter " Search: \"\"") -(defvar-local company-search-old-map nil) +(defvar-local company-search-filtering nil + "Non-nil to filter the completion candidates by the search string") -(defvar-local company-search-old-selection 0) +(defvar-local company--search-old-selection 0) -(defun company-search (text lines) +(defun company--search (text lines) (let ((quoted (regexp-quote text)) (i 0)) (cl-dolist (line lines) @@ -1581,24 +1586,53 @@ from the rest of the back-ends in the group, if any, will be left at the end." (cl-return i)) (cl-incf i)))) +(defun company-search-keypad () + (interactive) + (let* ((name (symbol-name last-command-event)) + (last-command-event (aref name (1- (length name))))) + (company-search-printing-char))) + (defun company-search-printing-char () (interactive) - (company-search-assert-enabled) - (let* ((ss (concat company-search-string (string last-command-event))) - (pos (company-search ss (nthcdr company-selection company-candidates)))) + (company--search-assert-enabled) + (let ((ss (concat company-search-string (string last-command-event)))) + (when company-search-filtering + (company--search-update-predicate ss)) + (company--search-update-string ss))) + +(defun company--search-update-predicate (&optional ss) + (let* ((company-candidates-predicate + (and (not (string= ss "")) + company-search-filtering + (lambda (candidate) (string-match ss candidate)))) + (cc (company-calculate-candidates company-prefix))) + (unless cc (error "No match")) + (company-update-candidates cc))) + +(defun company--search-update-string (new) + (let* ((pos (company--search new (nthcdr company-selection company-candidates)))) (if (null pos) (ding) - (setq company-search-string ss - company-search-lighter (concat " Search: \"" ss "\"")) + (setq company-search-string new + company-search-lighter (format " %s: \"%s\"" + (if company-search-filtering + "Filter" + "Search") + new)) (company-set-selection (+ company-selection pos) t)))) +(defun company--search-assert-input () + (company--search-assert-enabled) + (when (string= company-search-string "") + (error "Empty search string"))) + (defun company-search-repeat-forward () "Repeat the incremental search in completion candidates forward." (interactive) - (company-search-assert-enabled) - (let ((pos (company-search company-search-string - (cdr (nthcdr company-selection - company-candidates))))) + (company--search-assert-input) + (let ((pos (company--search company-search-string + (cdr (nthcdr company-selection + company-candidates))))) (if (null pos) (ding) (company-set-selection (+ company-selection pos 1) t)))) @@ -1606,52 +1640,47 @@ from the rest of the back-ends in the group, if any, will be left at the end." (defun company-search-repeat-backward () "Repeat the incremental search in completion candidates backwards." (interactive) - (company-search-assert-enabled) - (let ((pos (company-search company-search-string - (nthcdr (- company-candidates-length - company-selection) - (reverse company-candidates))))) + (company--search-assert-input) + (let ((pos (company--search company-search-string + (nthcdr (- company-candidates-length + company-selection) + (reverse company-candidates))))) (if (null pos) (ding) (company-set-selection (- company-selection pos 1) t)))) -(defun company-create-match-predicate () - (let ((ss company-search-string)) - (setq company-candidates-predicate - (when ss (lambda (candidate) (string-match ss candidate))))) - (company-update-candidates - (company-apply-predicate company-candidates company-candidates-predicate)) - ;; Invalidate cache. - (setq company-candidates-cache (cons company-prefix company-candidates))) - -(defun company-filter-printing-char () - (interactive) - (company-search-assert-enabled) - (company-search-printing-char) - (company-create-match-predicate) - (company-call-frontends 'update)) - -(defun company-search-kill-others () - "Limit the completion candidates to the ones matching the search string." +(defun company-search-toggle-filtering () + "Toggle `company-search-filtering'." (interactive) - (company-search-assert-enabled) - (company-create-match-predicate) - (company-search-mode 0) - (company-call-frontends 'update)) + (company--search-assert-enabled) + (setq company-search-filtering (not company-search-filtering)) + (let ((ss company-search-string)) + (company--search-update-predicate ss) + (company--search-update-string ss))) (defun company-search-abort () "Abort searching the completion candidates." (interactive) - (company-search-assert-enabled) - (company-set-selection company-search-old-selection t) - (company-search-mode 0)) + (company--search-assert-enabled) + (company-search-mode 0) + (company-set-selection company--search-old-selection t)) (defun company-search-other-char () (interactive) - (company-search-assert-enabled) + (company--search-assert-enabled) (company-search-mode 0) (company--unread-last-input)) +(defun company-search-delete-char () + (interactive) + (company--search-assert-enabled) + (if (string= company-search-string "") + (ding) + (let ((ss (substring company-search-string 0 -1))) + (when company-search-filtering + (company--search-update-predicate ss)) + (company--search-update-string ss)))) + (defvar company-search-map (let ((i 0) (keymap (make-keymap))) @@ -1672,18 +1701,22 @@ from the rest of the back-ends in the group, if any, will be left at the end." (while (< i 256) (define-key keymap (vector i) 'company-search-printing-char) (cl-incf i)) + (dotimes (i 10) + (define-key keymap (read (format "[kp-%s]" i)) 'company-search-keypad)) (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 (kbd "M-n") 'company-select-next) + (define-key keymap (kbd "M-p") 'company-select-previous) (define-key keymap "\e\e\e" '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 (kbd "DEL") 'company-search-delete-char) + (define-key keymap [backspace] 'company-search-delete-char) (define-key keymap "\C-g" 'company-search-abort) (define-key keymap "\C-s" 'company-search-repeat-forward) (define-key keymap "\C-r" 'company-search-repeat-backward) - (define-key keymap "\C-o" 'company-search-kill-others) + (define-key keymap "\C-o" 'company-search-toggle-filtering) keymap) "Keymap used for incrementally searching the completion candidates.") @@ -1695,15 +1728,19 @@ Don't start this directly, use `company-search-candidates' or (if company-search-mode (if (company-manual-begin) (progn - (setq company-search-old-selection company-selection) + (setq company--search-old-selection company-selection) (company-call-frontends 'update)) (setq company-search-mode nil)) (kill-local-variable 'company-search-string) (kill-local-variable 'company-search-lighter) - (kill-local-variable 'company-search-old-selection) + (kill-local-variable 'company-search-filtering) + (kill-local-variable 'company--search-old-selection) + (when company-backend + (company--search-update-predicate "") + (company-call-frontends 'update)) (company-enable-overriding-keymap company-active-map))) -(defun company-search-assert-enabled () +(defun company--search-assert-enabled () (company-assert-enabled) (unless company-search-mode (company-uninstall-map) @@ -1716,11 +1753,12 @@ Don't start this directly, use `company-search-candidates' or - `company-search-repeat-forward' (\\[company-search-repeat-forward]) - `company-search-repeat-backward' (\\[company-search-repeat-backward]) - `company-search-abort' (\\[company-search-abort]) +- `company-search-delete-char' (\\[company-search-delete-char]) 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-toggle-filtering' (\\[company-search-toggle-filtering]) +uses the search string to filter the completion candidates." (interactive) (company-search-mode 1) (company-enable-overriding-keymap company-search-map)) @@ -1736,10 +1774,10 @@ uses the search string to limit the completion candidates." (defun company-filter-candidates () "Start filtering the completion candidates incrementally. This works the same way as `company-search-candidates' immediately -followed by `company-search-kill-others' after each input." +followed by `company-search-toggle-filtering'." (interactive) (company-search-mode 1) - (company-enable-overriding-keymap company-filter-map)) + (setq company-search-filtering t)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1843,6 +1881,16 @@ and invoke the normal binding." (when company-common (company--insert-candidate company-common))))) +(defun company-complete-common-or-cycle () + "Insert the common part of all candidates, or select the next one." + (interactive) + (when (company-manual-begin) + (let ((tick (buffer-chars-modified-tick))) + (call-interactively 'company-complete-common) + (when (eq tick (buffer-chars-modified-tick)) + (let ((company-selection-wrap-around t)) + (call-interactively 'company-select-next)))))) + (defun company-complete () "Insert the common part of all candidates or the current selection. The first time this is called, the common part is inserted, the second @@ -1857,18 +1905,26 @@ inserted." (setq this-command 'company-complete-common)))) (defun company-complete-number (n) - "Insert the Nth candidate. + "Insert the Nth candidate visible in the tooltip. To show the number next to the candidates in some back-ends, enable `company-show-numbers'. When called interactively, uses the last typed character, stripping the modifiers. That character must be a digit." (interactive - (list (let ((n (- (event-basic-type last-command-event) ?0))) + (list (let* ((type (event-basic-type last-command-event)) + (char (if (characterp type) + ;; Number on the main row. + type + ;; Keypad number, if bound directly. + (car (last (string-to-list (symbol-name type)))))) + (n (- char ?0))) (if (zerop n) 10 n)))) (when (company-manual-begin) - (and (or (< n 1) (> n company-candidates-length)) + (and (or (< n 1) (> n (- company-candidates-length + company-tooltip-offset))) (error "No candidate number %d" n)) (cl-decf n) - (company-finish (nth n company-candidates)))) + (company-finish (nth (+ n company-tooltip-offset) + company-candidates)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2095,7 +2151,6 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (string-width company-common) 0))) (ann-ralign company-tooltip-align-annotations) - (value (company--clean-string value)) (ann-truncate (< width (+ (length value) (length annotation) (if ann-ralign 1 0)))) @@ -2134,17 +2189,13 @@ If SHOW-VERSION is non-nil, show the version in the echo area." mouse-face company-tooltip-mouse) line)) (when selected - (if (and company-search-string + (if (and (not (string= 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-search) - line) - (when (< beg common) - (add-text-properties beg common - '(face company-tooltip-common-selection) - line))) + line)) (add-text-properties 0 width '(face company-tooltip-selection mouse-face company-tooltip-selection) line) @@ -2178,7 +2229,8 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (defun company-buffer-lines (beg end) (goto-char beg) (let (lines lines-moved) - (while (and (> (setq lines-moved (vertical-motion 1)) 0) + (while (and (not (eobp)) ; http://debbugs.gnu.org/19553 + (> (setq lines-moved (vertical-motion 1)) 0) (<= (point) end)) (let ((bound (min end (1- (point))))) ;; A visual line can contain several physical lines (e.g. with outline's @@ -2213,7 +2265,7 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (floor (window-screen-lines)) (window-body-height))) -(defsubst company--window-width () +(defun company--window-width () (let ((ww (window-body-width))) ;; Account for the line continuation column. (when (zerop (cadr (window-fringes))) @@ -2226,6 +2278,10 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (let ((margins (window-margins))) (+ (or (car margins) 0) (or (cdr margins) 0))))) + (when (and word-wrap + (version< emacs-version "24.4.51.5")) + ;; http://debbugs.gnu.org/18384 + (cl-decf ww)) ww)) (defun company--replacement-string (lines old column nl &optional align-top) @@ -2275,7 +2331,6 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (defun company--create-lines (selection limit) (let ((len company-candidates-length) - (numbered 99999) (window-width (company--window-width)) lines width @@ -2317,11 +2372,14 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (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))) + (setq value (company--clean-string value)) + (when annotation + (when company-tooltip-align-annotations + ;; `lisp-completion-at-point' adds a space. + (setq annotation (comment-string-strip annotation t nil))) + (setq annotation (company--clean-string annotation))) (push (cons value annotation) items) - (setq width (max (+ (string-width value) + (setq width (max (+ (length value) (if (and annotation company-tooltip-align-annotations) (1+ (length annotation)) (length annotation))) @@ -2329,16 +2387,13 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (setq width (min window-width (max company-tooltip-minimum-width - (if (and company-show-numbers - (< company-tooltip-offset 10)) + (if company-show-numbers (+ 2 width) width)))) - ;; number can make tooltip too long - (when company-show-numbers - (setq numbered company-tooltip-offset)) - - (let ((items (nreverse items)) new) + (let ((items (nreverse items)) + (numbered (if company-show-numbers 0 99999)) + new) (when previous (push (company--scrollpos-line previous width) new)) @@ -2414,7 +2469,7 @@ Returns a negative number if the tooltip should be displayed above point." (end (save-excursion (move-to-window-line (+ row (abs height))) (point))) - (ov (make-overlay (if nl beg (1- beg)) end nil t t)) + (ov (make-overlay (if nl beg (1- beg)) end nil t)) (args (list (mapcar 'company-plainify (company-buffer-lines beg end)) column nl above))) @@ -2519,8 +2574,6 @@ Returns a negative number if the tooltip should be displayed above point." (defun company-preview-show-at-point (pos) (company-preview-hide) - (setq company-preview-overlay (make-overlay pos pos)) - (let ((completion (nth company-selection company-candidates))) (setq completion (propertize completion 'face 'company-preview)) (add-text-properties 0 (length company-common) @@ -2538,11 +2591,26 @@ Returns a negative number if the tooltip should be displayed above point." (and (equal pos (point)) (not (equal completion "")) - (add-text-properties 0 1 '(cursor t) completion)) - - (let ((ov company-preview-overlay)) - (overlay-put ov 'after-string completion) - (overlay-put ov 'window (selected-window))))) + (add-text-properties 0 1 '(cursor 1) completion)) + + (let* ((beg pos) + (pto company-pseudo-tooltip-overlay) + (ptf-workaround (and + pto + (char-before pos) + (eq pos (overlay-start pto))))) + ;; Try to accomodate for the pseudo-tooltip overlay, + ;; which may start at the same position if it's at eol. + (when ptf-workaround + (cl-decf beg) + (setq completion (concat (buffer-substring beg pos) completion))) + + (setq company-preview-overlay (make-overlay beg pos)) + + (let ((ov company-preview-overlay)) + (overlay-put ov (if ptf-workaround 'display 'after-string) + completion) + (overlay-put ov 'window (selected-window)))))) (defun company-preview-hide () (when company-preview-overlay diff --git a/packages/company/test/all.el b/packages/company/test/all.el new file mode 100644 index 0000000..6d64a62 --- /dev/null +++ b/packages/company/test/all.el @@ -0,0 +1,28 @@ +;;; all-tests.el --- company-mode tests -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Dmitry Gutov + +;; 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/>. + +(defvar company-test-path + (file-name-directory (or load-file-name buffer-file-name))) + +(require 'ert) + +(dolist (test-file (directory-files company-test-path t "-tests.el$")) + (load test-file nil t)) diff --git a/packages/company/test/async-tests.el b/packages/company/test/async-tests.el new file mode 100644 index 0000000..5d8be3e --- /dev/null +++ b/packages/company/test/async-tests.el @@ -0,0 +1,161 @@ +;;; async-tests.el --- company-mode tests -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Dmitry Gutov + +;; 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) + +(defun company-async-backend (command &optional _) + (pcase command + (`prefix "foo") + (`candidates + (cons :async + (lambda (cb) + (run-with-timer 0.05 nil + #'funcall cb '("abc" "abd"))))))) + +(ert-deftest company-call-backend-forces-sync () + (let ((company-backend 'company-async-backend) + (company-async-timeout 0.1)) + (should (equal '("abc" "abd") (company-call-backend 'candidates))))) + +(ert-deftest company-call-backend-errors-on-timeout () + (with-temp-buffer + (let* ((company-backend (lambda (command &optional _arg) + (pcase command + (`candidates (cons :async 'ignore))))) + (company-async-timeout 0.1) + (err (should-error (company-call-backend 'candidates "foo")))) + (should (string-match-p "async timeout" (cadr err)))))) + +(ert-deftest company-call-backend-raw-passes-return-value-verbatim () + (let ((company-backend 'company-async-backend)) + (should (equal "foo" (company-call-backend-raw 'prefix))) + (should (equal :async (car (company-call-backend-raw 'candidates "foo")))) + (should (equal 'closure (cadr (company-call-backend-raw 'candidates "foo")))))) + +(ert-deftest company-manual-begin-forces-async-candidates-to-sync () + (with-temp-buffer + (company-mode) + (let (company-frontends + company-transformers + (company-backends (list 'company-async-backend))) + (company-manual-begin) + (should (equal "foo" company-prefix)) + (should (equal '("abc" "abd") company-candidates))))) + +(ert-deftest company-idle-begin-allows-async-candidates () + (with-temp-buffer + (company-mode) + (let (company-frontends + company-transformers + (company-backends (list 'company-async-backend))) + (company-idle-begin (current-buffer) (selected-window) + (buffer-chars-modified-tick) (point)) + (should (null company-candidates)) + (sleep-for 0.1) + (should (equal "foo" company-prefix)) + (should (equal '("abc" "abd") company-candidates))))) + +(ert-deftest company-idle-begin-cancels-async-candidates-if-buffer-changed () + (with-temp-buffer + (company-mode) + (let (company-frontends + (company-backends (list 'company-async-backend))) + (company-idle-begin (current-buffer) (selected-window) + (buffer-chars-modified-tick) (point)) + (should (null company-candidates)) + (insert "a") + (sleep-for 0.1) + (should (null company-candidates))))) + +(ert-deftest company-idle-begin-async-allows-immediate-callbacks () + (with-temp-buffer + (company-mode) + (let (company-frontends + (company-backends + (list (lambda (command &optional arg) + (pcase command + (`prefix (buffer-substring (point-min) (point))) + (`candidates + (let ((c (all-completions arg '("abc" "def")))) + (cons :async + (lambda (cb) (funcall cb c))))) + (`no-cache t))))) + (company-minimum-prefix-length 0)) + (company-idle-begin (current-buffer) (selected-window) + (buffer-chars-modified-tick) (point)) + (should (equal '("abc" "def") company-candidates)) + (let ((last-command-event ?a)) + (company-call 'self-insert-command 1)) + (should (equal '("abc") company-candidates))))) + +(ert-deftest company-multi-backend-forces-prefix-to-sync () + (with-temp-buffer + (let ((company-backend (list 'ignore + (lambda (command) + (should (eq command 'prefix)) + (cons :async + (lambda (cb) + (run-with-timer + 0.01 nil + (lambda () (funcall cb nil)))))) + (lambda (command) + (should (eq command 'prefix)) + "foo")))) + (should (equal "foo" (company-call-backend-raw 'prefix)))) + (let ((company-backend (list (lambda (_command) + (cons :async + (lambda (cb) + (run-with-timer + 0.01 nil + (lambda () (funcall cb "bar")))))) + (lambda (_command) + "foo")))) + (should (equal "bar" (company-call-backend-raw 'prefix)))))) + +(ert-deftest company-multi-backend-merges-deferred-candidates () + (with-temp-buffer + (let* ((immediate (lambda (command &optional _) + (pcase command + (`prefix "foo") + (`candidates + (cons :async + (lambda (cb) (funcall cb '("f")))))))) + (company-backend (list 'ignore + (lambda (command &optional arg) + (pcase command + (`prefix "foo") + (`candidates + (should (equal arg "foo")) + (cons :async + (lambda (cb) + (run-with-timer + 0.01 nil + (lambda () (funcall cb '("a" "b"))))))))) + (lambda (command &optional _) + (pcase command + (`prefix "foo") + (`candidates '("c" "d" "e")))) + immediate))) + (should (equal :async (car (company-call-backend-raw 'candidates "foo")))) + (should (equal '("a" "b" "c" "d" "e" "f") + (company-call-backend 'candidates "foo"))) + (let ((company-backend (list immediate))) + (should (equal '("f") (company-call-backend 'candidates "foo"))))))) diff --git a/packages/company/test/clang-tests.el b/packages/company/test/clang-tests.el new file mode 100644 index 0000000..c8c03b7 --- /dev/null +++ b/packages/company/test/clang-tests.el @@ -0,0 +1,25 @@ +(require 'company-tests) +(require 'company-clang) + +(ert-deftest company-clang-objc-templatify () + (with-temp-buffer + (let ((text "createBookWithTitle:andAuthor:")) + (insert text) + (company-clang-objc-templatify text) + (should (equal "createBookWithTitle:arg0 andAuthor:arg1" (buffer-string))) + (should (looking-at "arg0")) + (should (null (overlay-get (company-template-field-at) 'display)))))) + +(ert-deftest company-clang-simple-annotation () + (let ((str (propertize + "foo" 'meta + "wchar_t * wmemchr(wchar_t *__p, wchar_t __c, size_t __n)"))) + (should (equal (company-clang 'annotation str) + "(wchar_t *__p, wchar_t __c, size_t __n)")))) + +(ert-deftest company-clang-generic-annotation () + (let ((str (propertize + "foo" 'meta + "shared_ptr<_Tp> make_shared<typename _Tp>(_Args &&__args...)"))) + (should (equal (company-clang 'annotation str) + "<typename _Tp>(_Args &&__args...)")))) diff --git a/packages/company/test/core-tests.el b/packages/company/test/core-tests.el new file mode 100644 index 0000000..13e547e --- /dev/null +++ b/packages/company/test/core-tests.el @@ -0,0 +1,481 @@ +;;; core-tests.el --- company-mode tests -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Dmitry Gutov + +;; 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) + +(ert-deftest company-good-prefix () + (let ((company-minimum-prefix-length 5) + company-abort-manual-when-too-short + company--manual-action ;idle begin + (company-selection-changed t)) ;has no effect + (should (eq t (company--good-prefix-p "!@#$%"))) + (should (eq nil (company--good-prefix-p "abcd"))) + (should (eq nil (company--good-prefix-p 'stop))) + (should (eq t (company--good-prefix-p '("foo" . 5)))) + (should (eq nil (company--good-prefix-p '("foo" . 4)))) + (should (eq t (company--good-prefix-p '("foo" . t)))))) + +(ert-deftest company--manual-prefix-set-and-unset () + (with-temp-buffer + (insert "ab") + (company-mode) + (let (company-frontends + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("abc" "abd"))))))) + (company-manual-begin) + (should (equal "ab" company--manual-prefix)) + (company-abort) + (should (null company--manual-prefix))))) + +(ert-deftest company-abort-manual-when-too-short () + (let ((company-minimum-prefix-length 5) + (company-abort-manual-when-too-short t) + (company-selection-changed t)) ;has not effect + (let ((company--manual-action nil)) ;idle begin + (should (eq t (company--good-prefix-p "!@#$%"))) + (should (eq t (company--good-prefix-p '("foo" . 5)))) + (should (eq t (company--good-prefix-p '("foo" . t))))) + (let ((company--manual-action t) + (company--manual-prefix "abc")) ;manual begin from this prefix + (should (eq t (company--good-prefix-p "!@#$"))) + (should (eq nil (company--good-prefix-p "ab"))) + (should (eq nil (company--good-prefix-p 'stop))) + (should (eq t (company--good-prefix-p '("foo" . 4)))) + (should (eq t (company--good-prefix-p "abcd"))) + (should (eq t (company--good-prefix-p "abc"))) + (should (eq t (company--good-prefix-p '("bar" . t))))))) + +(ert-deftest company-common-with-non-prefix-completion () + (let ((company-backend #'ignore) + (company-prefix "abc") + company-candidates + company-candidates-length + company-candidates-cache + company-common) + (company-update-candidates '("abc" "def-abc")) + (should (null company-common)) + (company-update-candidates '("abc" "abe-c")) + (should (null company-common)) + (company-update-candidates '("abcd" "abcde" "abcdf")) + (should (equal "abcd" company-common)))) + +(ert-deftest company-multi-backend-with-lambdas () + (let ((company-backend + (list (lambda (command &optional _ &rest _r) + (cl-case command + (prefix "z") + (candidates '("a" "b")))) + (lambda (command &optional _ &rest _r) + (cl-case command + (prefix "z") + (candidates '("c" "d"))))))) + (should (equal (company-call-backend 'candidates "z") '("a" "b" "c" "d"))))) + +(ert-deftest company-multi-backend-filters-backends-by-prefix () + (let ((company-backend + (list (lambda (command &optional _ &rest _r) + (cl-case command + (prefix (cons "z" t)) + (candidates '("a" "b")))) + (lambda (command &optional _ &rest _r) + (cl-case command + (prefix "t") + (candidates '("c" "d")))) + (lambda (command &optional _ &rest _r) + (cl-case command + (prefix "z") + (candidates '("e" "f"))))))) + (should (equal (company-call-backend 'candidates "z") '("a" "b" "e" "f"))))) + +(ert-deftest company-multi-backend-remembers-candidate-backend () + (let ((company-backend + (list (lambda (command &optional _) + (cl-case command + (ignore-case nil) + (annotation "1") + (candidates '("a" "c")) + (post-completion "13"))) + (lambda (command &optional _) + (cl-case command + (ignore-case t) + (annotation "2") + (candidates '("b" "d")) + (post-completion "42"))) + (lambda (command &optional _) + (cl-case command + (annotation "3") + (candidates '("e")) + (post-completion "74")))))) + (let ((candidates (company-calculate-candidates nil))) + (should (equal candidates '("a" "b" "c" "d" "e"))) + (should (equal t (company-call-backend 'ignore-case))) + (should (equal "1" (company-call-backend 'annotation (nth 0 candidates)))) + (should (equal "2" (company-call-backend 'annotation (nth 1 candidates)))) + (should (equal "13" (company-call-backend 'post-completion (nth 2 candidates)))) + (should (equal "42" (company-call-backend 'post-completion (nth 3 candidates)))) + (should (equal "3" (company-call-backend 'annotation (nth 4 candidates)))) + (should (equal "74" (company-call-backend 'post-completion (nth 4 candidates))))))) + +(ert-deftest company-multi-backend-handles-keyword-with () + (let ((primo (lambda (command &optional _) + (cl-case command + (prefix "a") + (candidates '("abb" "abc" "abd"))))) + (secundo (lambda (command &optional _) + (cl-case command + (prefix "a") + (candidates '("acc" "acd")))))) + (let ((company-backend (list 'ignore 'ignore :with secundo))) + (should (null (company-call-backend 'prefix)))) + (let ((company-backend (list 'ignore primo :with secundo))) + (should (equal "a" (company-call-backend 'prefix))) + (should (equal '("abb" "abc" "abd" "acc" "acd") + (company-call-backend 'candidates "a")))))) + +(ert-deftest company-begin-backend-failure-doesnt-break-company-backends () + (with-temp-buffer + (insert "a") + (company-mode) + (should-error + (company-begin-backend #'ignore)) + (let (company-frontends + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix "a") + (candidates '("a" "ab" "ac"))))))) + (let (this-command) + (company-call 'complete)) + (should (eq 3 company-candidates-length))))) + +(ert-deftest company-require-match-explicit () + (with-temp-buffer + (insert "ab") + (company-mode) + (let (company-frontends + (company-require-match 'company-explicit-action-p) + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("abc" "abd"))))))) + (let (this-command) + (company-complete)) + (let ((last-command-event ?e)) + (company-call 'self-insert-command 1)) + (should (eq 2 company-candidates-length)) + (should (eq 3 (point)))))) + +(ert-deftest company-dont-require-match-when-idle () + (with-temp-buffer + (insert "ab") + (company-mode) + (let (company-frontends + (company-minimum-prefix-length 2) + (company-require-match 'company-explicit-action-p) + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("abc" "abd"))))))) + (company-idle-begin (current-buffer) (selected-window) + (buffer-chars-modified-tick) (point)) + (should (eq 2 company-candidates-length)) + (let ((last-command-event ?e)) + (company-call 'self-insert-command 1)) + (should (eq nil company-candidates-length)) + (should (eq 4 (point)))))) + +(ert-deftest company-dont-require-match-if-was-a-match-and-old-prefix-ended () + (with-temp-buffer + (insert "ab") + (company-mode) + (let (company-frontends + company-auto-complete + (company-require-match t) + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (company-grab-word)) + (candidates '("abc" "ab" "abd")) + (sorted t)))))) + (let (this-command) + (company-complete)) + (let ((last-command-event ?e)) + (company-call 'self-insert-command 1)) + (should (eq 3 company-candidates-length)) + (should (eq 3 (point))) + (let ((last-command-event ? )) + (company-call 'self-insert-command 1)) + (should (null company-candidates-length)) + (should (eq 4 (point)))))) + +(ert-deftest company-dont-require-match-if-was-a-match-and-new-prefix-is-stop () + (with-temp-buffer + (company-mode) + (insert "c") + (let (company-frontends + (company-require-match t) + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (if (> (point) 2) + 'stop + (buffer-substring (point-min) (point)))) + (candidates '("a" "b" "c"))))))) + (let (this-command) + (company-complete)) + (should (eq 3 company-candidates-length)) + (let ((last-command-event ?e)) + (company-call 'self-insert-command 1)) + (should (not company-candidates))))) + +(ert-deftest company-should-complete-whitelist () + (with-temp-buffer + (insert "ab") + (company-mode) + (let (company-frontends + company-begin-commands + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("abc" "abd"))))))) + (let ((company-continue-commands nil)) + (let (this-command) + (company-complete)) + (company-call 'backward-delete-char 1) + (should (null company-candidates-length))) + (let ((company-continue-commands '(backward-delete-char))) + (let (this-command) + (company-complete)) + (company-call 'backward-delete-char 1) + (should (eq 2 company-candidates-length)))))) + +(ert-deftest company-should-complete-blacklist () + (with-temp-buffer + (insert "ab") + (company-mode) + (let (company-frontends + company-begin-commands + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("abc" "abd"))))))) + (let ((company-continue-commands '(not backward-delete-char))) + (let (this-command) + (company-complete)) + (company-call 'backward-delete-char 1) + (should (null company-candidates-length))) + (let ((company-continue-commands '(not backward-delete-char-untabify))) + (let (this-command) + (company-complete)) + (company-call 'backward-delete-char 1) + (should (eq 2 company-candidates-length)))))) + +(ert-deftest company-auto-complete-explicit () + (with-temp-buffer + (insert "ab") + (company-mode) + (let (company-frontends + (company-auto-complete 'company-explicit-action-p) + (company-auto-complete-chars '(? )) + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("abcd" "abef"))))))) + (let (this-command) + (company-complete)) + (let ((last-command-event ? )) + (company-call 'self-insert-command 1)) + (should (string= "abcd " (buffer-string)))))) + +(ert-deftest company-no-auto-complete-when-idle () + (with-temp-buffer + (insert "ab") + (company-mode) + (let (company-frontends + (company-auto-complete 'company-explicit-action-p) + (company-auto-complete-chars '(? )) + (company-minimum-prefix-length 2) + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("abcd" "abef"))))))) + (company-idle-begin (current-buffer) (selected-window) + (buffer-chars-modified-tick) (point)) + (let ((last-command-event ? )) + (company-call 'self-insert-command 1)) + (should (string= "ab " (buffer-string)))))) + +(ert-deftest company-clears-explicit-action-when-no-matches () + (with-temp-buffer + (company-mode) + (let (company-frontends + company-backends) + (company-call 'manual-begin) ;; fails + (should (null company-candidates)) + (should (null (company-explicit-action-p)))))) + +(ert-deftest company-ignore-case-replaces-prefix () + (with-temp-buffer + (company-mode) + (let (company-frontends + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("abcd" "abef")) + (ignore-case t)))))) + (insert "A") + (let (this-command) + (company-complete)) + (should (string= "ab" (buffer-string))) + (delete-char -2) + (insert "A") ; hack, to keep it in one test + (company-complete-selection) + (should (string= "abcd" (buffer-string)))))) + +(ert-deftest company-ignore-case-with-keep-prefix () + (with-temp-buffer + (insert "AB") + (company-mode) + (let (company-frontends + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("abcd" "abef")) + (ignore-case 'keep-prefix)))))) + (let (this-command) + (company-complete)) + (company-complete-selection) + (should (string= "ABcd" (buffer-string)))))) + +(ert-deftest company-non-prefix-completion () + (with-temp-buffer + (insert "tc") + (company-mode) + (let (company-frontends + (company-backends + (list (lambda (command &optional _) + (cl-case command + (prefix (buffer-substring (point-min) (point))) + (candidates '("tea-cup" "teal-color"))))))) + (let (this-command) + (company-complete)) + (should (string= "tc" (buffer-string))) + (company-complete-selection) + (should (string= "tea-cup" (buffer-string)))))) + +(defvar ct-sorted nil) + +(defun ct-equal-including-properties (list1 list2) + (or (and (not list1) (not list2)) + (and (ert-equal-including-properties (car list1) (car list2)) + (ct-equal-including-properties (cdr list1) (cdr list2))))) + +(ert-deftest company-strips-duplicates-within-groups () + (let* ((kvs '(("a" . "b") + ("a" . nil) + ("a" . "b") + ("a" . "c") + ("a" . "b") + ("b" . "c") + ("b" . nil) + ("a" . "b"))) + (fn (lambda (kvs) + (mapcar (lambda (kv) (propertize (car kv) 'ann (cdr kv))) + kvs))) + (company-backend + (lambda (command &optional arg) + (pcase command + (`prefix "") + (`sorted ct-sorted) + (`duplicates t) + (`annotation (get-text-property 0 'ann arg))))) + (reference '(("a" . "b") + ("a" . nil) + ("a" . "c") + ("b" . "c") + ("b" . nil) + ("a" . "b")))) + (let ((ct-sorted t)) + (should (ct-equal-including-properties + (company--preprocess-candidates (funcall fn kvs)) + (funcall fn reference)))) + (should (ct-equal-including-properties + (company--preprocess-candidates (funcall fn kvs)) + (funcall fn (butlast reference)))))) + +;;; Row and column + +(ert-deftest company-column-with-composition () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert "lambda ()") + (compose-region 1 (1+ (length "lambda")) "\\") + (should (= (company--column) 4))))) + +(ert-deftest company-column-with-line-prefix () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert "foo") + (put-text-property (point-min) (point) 'line-prefix " ") + (should (= (company--column) 5))))) + +(ert-deftest company-column-with-line-prefix-on-empty-line () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert "\n") + (forward-char -1) + (put-text-property (point-min) (point-max) 'line-prefix " ") + (should (= (company--column) 2))))) + +(ert-deftest company-column-with-tabs () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert "|\t|\t|\t(") + (let ((tab-width 8)) + (should (= (company--column) 25)))))) + +(ert-deftest company-row-with-header-line-format () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (should (= (company--row) 0)) + (setq header-line-format "aaaaaaa") + (should (= (company--row) 0))))) diff --git a/packages/company/company-elisp-tests.el b/packages/company/test/elisp-tests.el similarity index 97% rename from packages/company/company-elisp-tests.el rename to packages/company/test/elisp-tests.el index 9b7cba3..7fd02de 100644 --- a/packages/company/company-elisp-tests.el +++ b/packages/company/test/elisp-tests.el @@ -1,6 +1,6 @@ -;;; company-elisp-tests.el --- company-elisp tests +;;; elisp-tests.el --- company-elisp tests -;; Copyright (C) 2013-2014 Free Software Foundation, Inc. +;; Copyright (C) 2013-2015 Free Software Foundation, Inc. ;; Author: Dmitry Gutov @@ -19,12 +19,9 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: -;; - ;;; Code: +(require 'company-tests) (require 'company-elisp) (defmacro company-elisp-with-buffer (contents &rest body) diff --git a/packages/company/test/frontends-tests.el b/packages/company/test/frontends-tests.el new file mode 100644 index 0000000..a10f914 --- /dev/null +++ b/packages/company/test/frontends-tests.el @@ -0,0 +1,298 @@ +;;; frontends-tests.el --- company-mode tests -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Dmitry Gutov + +;; 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) + +(ert-deftest company-pseudo-tooltip-does-not-get-displaced () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (save-excursion (insert " ff")) + (company-mode) + (let ((company-frontends '(company-pseudo-tooltip-frontend)) + (company-begin-commands '(self-insert-command)) + (company-backends + (list (lambda (c &optional _) + (cl-case c (prefix "") (candidates '("a" "b" "c"))))))) + (let (this-command) + (company-call 'complete)) + (company-call 'open-line 1) + (should (eq 1 (overlay-start company-pseudo-tooltip-overlay))))))) + +(ert-deftest company-pseudo-tooltip-show () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert "aaaa\n bb\nccccccc\nddd") + (search-backward "bb") + (let ((col (company--column)) + (company-candidates-length 2) + (company-candidates '("123" "45")) + (company-backend 'ignore)) + (company-pseudo-tooltip-show (company--row) col 0) + (let ((ov company-pseudo-tooltip-overlay)) + ;; With margins. + (should (eq (overlay-get ov 'company-width) 5)) + ;; FIXME: Make it 2? + (should (eq (overlay-get ov 'company-height) company-tooltip-limit)) + (should (eq (overlay-get ov 'company-column) col)) + (should (string= (overlay-get ov 'company-display) + "\n 123 \nc 45 c\nddd\n"))))))) + +(ert-deftest company-pseudo-tooltip-edit-updates-width () + :tags '(interactive) + (with-temp-buffer + (set-window-buffer nil (current-buffer)) + (let ((company-candidates-length 5) + (company-candidates '("123" "45" "67" "89" "1011")) + (company-backend 'ignore) + (company-tooltip-limit 4) + (company-tooltip-offset-display 'scrollbar)) + (company-pseudo-tooltip-show (company--row) + (company--column) + 0) + (should (eq (overlay-get company-pseudo-tooltip-overlay 'company-width) + 6)) + (company-pseudo-tooltip-edit 4) + (should (eq (overlay-get company-pseudo-tooltip-overlay 'company-width) + 7))))) + +(ert-deftest company-preview-show-with-annotations () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (save-excursion (insert "\n")) + (let ((company-candidates-length 1) + (company-candidates '("123"))) + (company-preview-show-at-point (point)) + (let* ((ov company-preview-overlay) + (str (overlay-get ov 'after-string))) + (should (string= str "123")) + (should (eq (get-text-property 0 'cursor str) 1))))))) + +(ert-deftest company-pseudo-tooltip-show-with-annotations () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert " ") + (save-excursion (insert "\n")) + (let ((company-candidates-length 2) + (company-backend (lambda (action &optional arg &rest _ignore) + (when (eq action 'annotation) + (cdr (assoc arg '(("123" . "(4)"))))))) + (company-candidates '("123" "45")) + company-tooltip-align-annotations) + (company-pseudo-tooltip-show-at-point (point) 0) + (let ((ov company-pseudo-tooltip-overlay)) + ;; With margins. + (should (eq (overlay-get ov 'company-width) 8)) + (should (string= (overlay-get ov 'company-display) + "\n 123(4) \n 45 \n"))))))) + +(ert-deftest company-pseudo-tooltip-show-with-annotations-right-aligned () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert " ") + (save-excursion (insert "\n")) + (let ((company-candidates-length 3) + (company-backend (lambda (action &optional arg &rest _ignore) + (when (eq action 'annotation) + (cdr (assoc arg '(("123" . "(4)") + ("67" . "(891011)"))))))) + (company-candidates '("123" "45" "67")) + (company-tooltip-align-annotations t)) + (company-pseudo-tooltip-show-at-point (point) 0) + (let ((ov company-pseudo-tooltip-overlay)) + ;; With margins. + (should (eq (overlay-get ov 'company-width) 13)) + (should (string= (overlay-get ov 'company-display) + "\n 123 (4) \n 45 \n 67 (891011) \n"))))))) + +(ert-deftest company-create-lines-shows-numbers () + (let ((company-show-numbers t) + (company-candidates '("x" "y" "z")) + (company-candidates-length 3) + (company-backend 'ignore)) + (should (equal '(" x 1 " " y 2 " " z 3 ") + (company--create-lines 0 999))))) + +(ert-deftest company-create-lines-truncates-annotations () + (let* ((ww (company--window-width)) + (data `(("1" . "(123)") + ("2" . nil) + ("3" . ,(concat "(" (make-string (- ww 2) ?4) ")")) + (,(make-string ww ?4) . "<4>"))) + (company-candidates (mapcar #'car data)) + (company-candidates-length 4) + (company-tooltip-margin 1) + (company-backend (lambda (cmd &optional arg) + (when (eq cmd 'annotation) + (cdr (assoc arg data))))) + company-tooltip-align-annotations) + (should (equal (list (format " 1(123)%s " (company-space-string (- ww 8))) + (format " 2%s " (company-space-string (- ww 3))) + (format " 3(444%s " (make-string (- ww 7) ?4)) + (format " %s " (make-string (- ww 2) ?4))) + (company--create-lines 0 999))) + (let ((company-tooltip-align-annotations t)) + (should (equal (list (format " 1%s(123) " (company-space-string (- ww 8))) + (format " 2%s " (company-space-string (- ww 3))) + (format " 3 (444%s " (make-string (- ww 8) ?4)) + (format " %s " (make-string (- ww 2) ?4))) + (company--create-lines 0 999)))))) + +(ert-deftest company-create-lines-truncates-common-part () + (let* ((ww (company--window-width)) + (company-candidates-length 2) + (company-tooltip-margin 1) + (company-backend #'ignore)) + (let* ((company-common (make-string (- ww 3) ?1)) + (company-candidates `(,(concat company-common "2") + ,(concat company-common "3")))) + (should (equal (list (format " %s2 " (make-string (- ww 3) ?1)) + (format " %s3 " (make-string (- ww 3) ?1))) + (company--create-lines 0 999)))) + (let* ((company-common (make-string (- ww 2) ?1)) + (company-candidates `(,(concat company-common "2") + ,(concat company-common "3")))) + (should (equal (list (format " %s " company-common) + (format " %s " company-common)) + (company--create-lines 0 999)))) + (let* ((company-common (make-string ww ?1)) + (company-candidates `(,(concat company-common "2") + ,(concat company-common "3"))) + (res (company--create-lines 0 999))) + (should (equal (list (format " %s " (make-string (- ww 2) ?1)) + (format " %s " (make-string (- ww 2) ?1))) + res)) + (should (eq 'company-tooltip-common-selection + (get-text-property (- ww 2) 'face + (car res)))) + (should (eq 'company-tooltip-selection + (get-text-property (1- ww) 'face + (car res)))) + ))) + +(ert-deftest company-create-lines-clears-out-non-printables () + :tags '(interactive) + (let (company-show-numbers + (company-candidates (list + (decode-coding-string "avalis\351e" 'utf-8) + "avatar")) + (company-candidates-length 2) + (company-backend 'ignore)) + (should (equal '(" avalis‗e " + " avatar ") + (company--create-lines 0 999))))) + +(ert-deftest company-create-lines-handles-multiple-width () + :tags '(interactive) + (let (company-show-numbers + (company-candidates '("蛙蛙蛙蛙" "蛙abc")) + (company-candidates-length 2) + (company-backend 'ignore)) + (should (equal '(" 蛙蛙蛙蛙 " + " 蛙abc ") + (company--create-lines 0 999))))) + +(ert-deftest company-create-lines-handles-multiple-width-in-annotation () + (let* (company-show-numbers + (alist '(("a" . " ︸") ("b" . " ︸︸"))) + (company-candidates (mapcar #'car alist)) + (company-candidates-length 2) + (company-backend (lambda (c &optional a) + (when (eq c 'annotation) + (assoc-default a alist))))) + (should (equal '(" a ︸ " + " b ︸︸ ") + (company--create-lines 0 999))))) + +(ert-deftest company-column-with-composition () + :tags '(interactive) + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert "lambda ()") + (compose-region 1 (1+ (length "lambda")) "\\") + (should (= (company--column) 4))))) + +(ert-deftest company-plainify () + (let ((tab-width 8)) + (should (equal-including-properties + (company-plainify "\tabc\td\t") + (concat " " + "abc " + "d ")))) + (should (equal-including-properties + (company-plainify (propertize "foobar" 'line-prefix "-*-")) + "-*-foobar"))) + +(ert-deftest company-buffer-lines-with-lines-folded () + :tags '(interactive) + (with-temp-buffer + (insert (propertize "aaa\nbbb\nccc\nddd\n" 'display "aaa+\n")) + (insert "eee\nfff\nggg") + (should (equal (company-buffer-lines (point-min) (point-max)) + '("aaa" "eee" "fff" "ggg"))))) + +(ert-deftest company-buffer-lines-with-multiline-display () + :tags '(interactive) + (with-temp-buffer + (insert (propertize "a" 'display "bbb\nccc\ndddd\n")) + (insert "eee\nfff\nggg") + (should (equal (company-buffer-lines (point-min) (point-max)) + '("" "" "" "eee" "fff" "ggg"))))) + +(ert-deftest company-buffer-lines-with-multiline-after-string-at-eob () + :tags '(interactive) + (with-temp-buffer + (insert "a\nb\nc\n") + (let ((ov (make-overlay (point-max) (point-max) nil t t))) + (overlay-put ov 'after-string "~\n~\n~")) + (should (equal (company-buffer-lines (point-min) (point-max)) + '("a" "b" "c"))))) + +(ert-deftest company-modify-line () + (let ((str "-*-foobar")) + (should (equal-including-properties + (company-modify-line str "zz" 4) + "-*-fzzbar")) + (should (equal-including-properties + (company-modify-line str "xx" 0) + "xx-foobar")) + (should (equal-including-properties + (company-modify-line str "zz" 10) + "-*-foobar zz")))) + +(ert-deftest company-scrollbar-bounds () + (should (equal nil (company--scrollbar-bounds 0 3 3))) + (should (equal nil (company--scrollbar-bounds 0 4 3))) + (should (equal '(0 . 0) (company--scrollbar-bounds 0 1 2))) + (should (equal '(1 . 1) (company--scrollbar-bounds 2 2 4))) + (should (equal '(2 . 3) (company--scrollbar-bounds 7 4 12))) + (should (equal '(1 . 2) (company--scrollbar-bounds 3 4 12))) + (should (equal '(1 . 3) (company--scrollbar-bounds 4 5 11)))) diff --git a/packages/company/test/keywords-tests.el b/packages/company/test/keywords-tests.el new file mode 100644 index 0000000..05843b2 --- /dev/null +++ b/packages/company/test/keywords-tests.el @@ -0,0 +1,32 @@ +;;; keywords-tests.el --- company-keywords tests -*- lexical-binding: t -*- + +;; Copyright (C) 2011, 2013-2015 Free Software Foundation, Inc. + +;; Author: Nikolaj Schumacher + +;; 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-keywords) + +(ert-deftest company-sorted-keywords () + "Test that keywords in `company-keywords-alist' are in alphabetical order." + (dolist (pair company-keywords-alist) + (when (consp (cdr pair)) + (let ((prev (cadr pair))) + (dolist (next (cddr pair)) + (should (not (equal prev next))) + (should (string< prev next)) + (setq prev next)))))) diff --git a/packages/company/test/template-tests.el b/packages/company/test/template-tests.el new file mode 100644 index 0000000..09548c4 --- /dev/null +++ b/packages/company/test/template-tests.el @@ -0,0 +1,91 @@ +;;; template-tests.el --- company-mode tests -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Dmitry Gutov + +;; 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-template) + +(ert-deftest company-template-removed-after-the-last-jump () + (with-temp-buffer + (insert "{ }") + (goto-char 2) + (let ((tpl (company-template-declare-template (point) (1- (point-max))))) + (save-excursion + (dotimes (_ 2) + (insert " ") + (company-template-add-field tpl (point) "foo"))) + (company-call 'template-forward-field) + (should (= 3 (point))) + (company-call 'template-forward-field) + (should (= 7 (point))) + (company-call 'template-forward-field) + (should (= 11 (point))) + (should (zerop (length (overlay-get tpl 'company-template-fields)))) + (should (null (overlay-buffer tpl)))))) + +(ert-deftest company-template-removed-after-input-and-jump () + (with-temp-buffer + (insert "{ }") + (goto-char 2) + (let ((tpl (company-template-declare-template (point) (1- (point-max))))) + (save-excursion + (insert " ") + (company-template-add-field tpl (point) "bar")) + (company-call 'template-move-to-first tpl) + (should (= 3 (point))) + (dolist (c (string-to-list "tee")) + (let ((last-command-event c)) + (company-call 'self-insert-command 1))) + (should (string= "{ tee }" (buffer-string))) + (should (overlay-buffer tpl)) + (company-call 'template-forward-field) + (should (= 7 (point))) + (should (null (overlay-buffer tpl)))))) + +(ert-deftest company-template-c-like-templatify () + (with-temp-buffer + (let ((text "foo(int a, short b)")) + (insert text) + (company-template-c-like-templatify text) + (should (equal "foo(arg0, arg1)" (buffer-string))) + (should (looking-at "arg0")) + (should (equal "int a" + (overlay-get (company-template-field-at) 'display)))))) + +(ert-deftest company-template-c-like-templatify-trims-after-closing-paren () + (with-temp-buffer + (let ((text "foo(int a, short b)!@ #1334 a")) + (insert text) + (company-template-c-like-templatify text) + (should (equal "foo(arg0, arg1)" (buffer-string))) + (should (looking-at "arg0"))))) + +(ert-deftest company-template-c-like-templatify-generics () + (with-temp-buffer + (let ((text "foo<TKey, TValue>(int i, Dict<TKey, TValue>, long l)")) + (insert text) + (company-template-c-like-templatify text) + (should (equal "foo<arg0, arg1>(arg2, arg3, arg4)" (buffer-string))) + (should (looking-at "arg0")) + (should (equal "TKey" (overlay-get (company-template-field-at) 'display))) + (search-forward "arg3") + (forward-char -1) + (should (equal "Dict<TKey, TValue>" + (overlay-get (company-template-field-at) 'display)))))) diff --git a/packages/company/test/transformers-tests.el b/packages/company/test/transformers-tests.el new file mode 100644 index 0000000..4d027e5 --- /dev/null +++ b/packages/company/test/transformers-tests.el @@ -0,0 +1,58 @@ +;;; transformers-tests.el --- company-mode tests -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Dmitry Gutov + +;; 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) + +(ert-deftest company-occurrence-prefer-closest-above () + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert "foo0 +foo1 +") + (save-excursion + (insert " +foo3 +foo2")) + (let ((company-backend 'company-dabbrev) + (company-occurrence-weight-function + 'company-occurrence-prefer-closest-above)) + (should (equal '("foo1" "foo0" "foo3" "foo2" "foo4") + (company-sort-by-occurrence + '("foo0" "foo1" "foo2" "foo3" "foo4")))))))) + +(ert-deftest company-occurrence-prefer-any-closest () + (with-temp-buffer + (save-window-excursion + (set-window-buffer nil (current-buffer)) + (insert "foo0 +foo1 +") + (save-excursion + (insert " +foo3 +foo2")) + (let ((company-backend 'company-dabbrev) + (company-occurrence-weight-function + 'company-occurrence-prefer-any-closest)) + (should (equal '("foo1" "foo3" "foo0" "foo2" "foo4") + (company-sort-by-occurrence + '("foo0" "foo1" "foo2" "foo3" "foo4"))))))))