Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
8.8 KiB

  1. ;;; company-capf.el --- company-mode completion-at-point-functions backend -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2013-2019 Free Software Foundation, Inc.
  3. ;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
  4. ;; This file is part of GNU Emacs.
  5. ;; GNU Emacs is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; GNU Emacs is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;
  17. ;; The CAPF back-end provides a bridge to the standard
  18. ;; completion-at-point-functions facility, and thus can support any major mode
  19. ;; that defines a proper completion function, including emacs-lisp-mode,
  20. ;; css-mode and nxml-mode.
  21. ;;; Code:
  22. (require 'company)
  23. (require 'cl-lib)
  24. ;; Amortizes several calls to a c-a-p-f from the same position.
  25. (defvar company--capf-cache nil)
  26. ;; FIXME: Provide a way to save this info once in Company itself
  27. ;; (https://github.com/company-mode/company-mode/pull/845).
  28. (defvar-local company-capf--current-completion-data nil
  29. "Value last returned by `company-capf' when called with `candidates'.
  30. For most properties/actions, this is just what we need: the exact values
  31. that accompanied the completion table that's currently is use.
  32. `company-capf', however, could be called at some different positions during
  33. a completion session (most importantly, by `company-sort-by-occurrence'),
  34. so we can't just use the preceding variable instead.")
  35. (defun company--capf-data ()
  36. (let ((cache company--capf-cache))
  37. (if (and (equal (current-buffer) (car cache))
  38. (equal (point) (car (setq cache (cdr cache))))
  39. (equal (buffer-chars-modified-tick) (car (setq cache (cdr cache)))))
  40. (cadr cache)
  41. (let ((data (company--capf-data-real)))
  42. (setq company--capf-cache
  43. (list (current-buffer) (point) (buffer-chars-modified-tick) data))
  44. data))))
  45. (defun company--capf-data-real ()
  46. (cl-letf* (((default-value 'completion-at-point-functions)
  47. ;; Ignore tags-completion-at-point-function because it subverts
  48. ;; company-etags in the default value of company-backends, where
  49. ;; the latter comes later.
  50. (remove 'tags-completion-at-point-function
  51. (default-value 'completion-at-point-functions)))
  52. (completion-at-point-functions (company--capf-workaround))
  53. (data (run-hook-wrapped 'completion-at-point-functions
  54. ;; Ignore misbehaving functions.
  55. #'completion--capf-wrapper 'optimist)))
  56. (when (and (consp (cdr data)) (integer-or-marker-p (nth 1 data))) data)))
  57. (declare-function python-shell-get-process "python")
  58. (defun company--capf-workaround ()
  59. ;; For http://debbugs.gnu.org/cgi/bugreport.cgi?bug=18067
  60. (if (or (not (listp completion-at-point-functions))
  61. (not (memq 'python-completion-complete-at-point completion-at-point-functions))
  62. (python-shell-get-process))
  63. completion-at-point-functions
  64. (remq 'python-completion-complete-at-point completion-at-point-functions)))
  65. (defun company-capf--save-current-data (data)
  66. (setq company-capf--current-completion-data data)
  67. (add-hook 'company-after-completion-hook
  68. #'company-capf--clear-current-data nil t))
  69. (defun company-capf--clear-current-data (_ignored)
  70. (setq company-capf--current-completion-data nil))
  71. (defvar-local company-capf--sorted nil)
  72. (defun company-capf (command &optional arg &rest _args)
  73. "`company-mode' backend using `completion-at-point-functions'."
  74. (interactive (list 'interactive))
  75. (pcase command
  76. (`interactive (company-begin-backend 'company-capf))
  77. (`prefix
  78. (let ((res (company--capf-data)))
  79. (when res
  80. (let ((length (plist-get (nthcdr 4 res) :company-prefix-length))
  81. (prefix (buffer-substring-no-properties (nth 1 res) (point))))
  82. (cond
  83. ((> (nth 2 res) (point)) 'stop)
  84. (length (cons prefix length))
  85. (t prefix))))))
  86. (`candidates
  87. (company-capf--candidates arg))
  88. (`sorted
  89. company-capf--sorted)
  90. (`match
  91. ;; Ask the for the `:company-match' function. If that doesn't help,
  92. ;; fallback to sniffing for face changes to get a suitable value.
  93. (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data)
  94. :company-match)))
  95. (if f (funcall f arg)
  96. (let* ((match-start nil) (pos -1)
  97. (prop-value nil) (faces nil)
  98. (has-face-p nil) chunks
  99. (limit (length arg)))
  100. (while (< pos limit)
  101. (setq pos
  102. (if (< pos 0) 0 (next-property-change pos arg limit)))
  103. (setq prop-value (or
  104. (get-text-property pos 'face arg)
  105. (get-text-property pos 'font-lock-face arg))
  106. faces (if (listp prop-value) prop-value (list prop-value))
  107. has-face-p (memq 'completions-common-part faces))
  108. (cond ((and (not match-start) has-face-p)
  109. (setq match-start pos))
  110. ((and match-start (not has-face-p))
  111. (push (cons match-start pos) chunks)
  112. (setq match-start nil))))
  113. (nreverse chunks)))))
  114. (`duplicates t)
  115. (`no-cache t) ;Not much can be done here, as long as we handle
  116. ;non-prefix matches.
  117. (`meta
  118. (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data)
  119. :company-docsig)))
  120. (when f (funcall f arg))))
  121. (`doc-buffer
  122. (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data)
  123. :company-doc-buffer)))
  124. (when f (funcall f arg))))
  125. (`location
  126. (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data)
  127. :company-location)))
  128. (when f (funcall f arg))))
  129. (`annotation
  130. (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data)
  131. :annotation-function)))
  132. (when f (funcall f arg))))
  133. (`require-match
  134. (plist-get (nthcdr 4 (company--capf-data)) :company-require-match))
  135. (`init nil) ;Don't bother: plenty of other ways to initialize the code.
  136. (`post-completion
  137. (company--capf-post-completion arg))
  138. ))
  139. (defun company-capf--candidates (input)
  140. (let ((res (company--capf-data)))
  141. (company-capf--save-current-data res)
  142. (when res
  143. (let* ((table (nth 3 res))
  144. (pred (plist-get (nthcdr 4 res) :predicate))
  145. (meta (completion-metadata
  146. (buffer-substring (nth 1 res) (nth 2 res))
  147. table pred))
  148. (candidates (completion-all-completions input table pred
  149. (length input)
  150. meta))
  151. (sortfun (cdr (assq 'display-sort-function meta)))
  152. (last (last candidates))
  153. (base-size (and (numberp (cdr last)) (cdr last))))
  154. (when base-size
  155. (setcdr last nil))
  156. (setq company-capf--sorted (functionp sortfun))
  157. (when sortfun
  158. (setq candidates (funcall sortfun candidates)))
  159. (if (not (zerop (or base-size 0)))
  160. (let ((before (substring input 0 base-size)))
  161. (mapcar (lambda (candidate)
  162. (concat before candidate))
  163. candidates))
  164. candidates)))))
  165. (defun company--capf-post-completion (arg)
  166. (let* ((res company-capf--current-completion-data)
  167. (exit-function (plist-get (nthcdr 4 res) :exit-function))
  168. (table (nth 3 res)))
  169. (if exit-function
  170. ;; We can more or less know when the user is done with completion,
  171. ;; so we do something different than `completion--done'.
  172. (funcall exit-function arg
  173. ;; FIXME: Should probably use an additional heuristic:
  174. ;; completion-at-point doesn't know when the user picked a
  175. ;; particular candidate explicitly (it only checks whether
  176. ;; further completions exist). Whereas company user can press
  177. ;; RET (or use implicit completion with company-tng).
  178. (if (= (car (completion-boundaries arg table nil ""))
  179. (length arg))
  180. 'sole
  181. 'finished)))))
  182. (provide 'company-capf)
  183. ;;; company-capf.el ends here