Klimi's new dotfiles with stow.
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.

237 lines
8.5 KiB

4 years ago
  1. ;;; ycmd-eldoc.el --- Eldoc support for ycmd-mode -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2016, 2017 Peter Vasil
  3. ;; Author: Peter Vasil <mail@petervasil.net>
  4. ;; URL: https://github.com/abingham/emacs-ycmd
  5. ;; Version: 0.2
  6. ;; Package-Requires: ((ycmd "1.3") (deferred "0.5.1") (s "1.11.0") (dash "2.13.0") (let-alist "1.0.5"))
  7. ;; This program is free software; you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; This program is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; To use this package, add these lines to your init.el file:
  19. ;;
  20. ;; (require 'ycmd-eldoc)
  21. ;; (add-hook 'ycmd-mode-hook 'ycmd-eldoc-setup)
  22. ;;
  23. ;;; Code:
  24. (eval-when-compile
  25. (require 'let-alist))
  26. (require 'eldoc)
  27. (require 'ycmd)
  28. (require 'deferred)
  29. (require 'dash)
  30. (require 's)
  31. (defgroup ycmd-eldoc nil
  32. "Eldoc support for `ycmd-mode'."
  33. :group 'ycmd
  34. :group 'eldoc)
  35. (defcustom ycmd-eldoc-always-semantic-server-query-modes
  36. '(not c-mode c++-mode objc-mode)
  37. "Modes for which `ycmd-eldoc' always queries semantic completion.
  38. If t, the ycmd server query is always semantic. If a list, server
  39. query is semantic for all `major-mode' symbols in that list. If
  40. the `car' of the list is `not', server query is sematic for all
  41. `major-mode' symbols _not_ in that list. If nil, the server query
  42. is only semantic after a semantic trigger."
  43. :type 'list)
  44. (defvar-local ycmd-eldoc--cache (make-vector 2 nil))
  45. (defvar-local ycmd-eldoc--cached-get-type-command 'none)
  46. (defun ycmd-eldoc--documentation-function ()
  47. "Eldoc function for `ycmd-mode'."
  48. (when (and ycmd-mode (not (ycmd-parsing-in-progress-p)))
  49. (deferred:$
  50. (ycmd-eldoc--check-if-semantic-completer-exists-for-mode)
  51. (deferred:nextc it
  52. (lambda (response)
  53. (when response
  54. (ycmd-eldoc--info-at-point))))
  55. (deferred:nextc it
  56. (lambda (text)
  57. (eldoc-message text))))
  58. ;; Don't show deferred object as ElDoc message
  59. nil))
  60. (defun ycmd-eldoc--check-if-semantic-completer-exists-for-mode ()
  61. "Return a deferred object whose return value is t if semantic completer exists."
  62. (deferred:$
  63. (deferred:next
  64. (lambda ()
  65. (ycmd-semantic-completer-available-p)))
  66. (deferred:nextc it
  67. (lambda (response)
  68. (when (and response (eq response 'none))
  69. (message (concat "No semantic completer exists for major-mode: `%s'."
  70. " Ycmd ELDoc mode disabled in current buffer.")
  71. major-mode)
  72. (ycmd-eldoc-mode -1))
  73. (eq response t)))))
  74. (defun ycmd-eldoc-always-semantic-server-query-p ()
  75. "Check whether server query should be semantic."
  76. (pcase ycmd-eldoc-always-semantic-server-query-modes
  77. (`t t)
  78. (`(not . ,modes) (not (memq major-mode modes)))
  79. (modes (memq major-mode modes))))
  80. (defmacro ycmd-eldoc--with-point-at-func-name (body)
  81. "Move cursor to function name and evluate BODY."
  82. (declare (indent 0) (debug t))
  83. `(save-excursion
  84. (ycmd-eldoc--goto-func-name)
  85. ,body))
  86. (defun ycmd-eldoc--info-at-point ()
  87. "Get function info at point."
  88. (let ((symbol (ycmd-eldoc--with-point-at-func-name (symbol-at-point))))
  89. (if (and symbol (eq symbol (aref ycmd-eldoc--cache 0)))
  90. (aref ycmd-eldoc--cache 1)
  91. (deferred:$
  92. (deferred:next
  93. (lambda ()
  94. (when symbol
  95. (ycmd-eldoc--with-point-at-func-name
  96. (let ((ycmd-force-semantic-completion
  97. (or ycmd-force-semantic-completion
  98. (ycmd-eldoc-always-semantic-server-query-p))))
  99. (ycmd-with-handled-server-exceptions
  100. (ycmd-get-completions)))))))
  101. (deferred:nextc it
  102. (lambda (completions)
  103. (-when-let (candidates (cdr (assq 'completions completions)))
  104. (ycmd-eldoc--generate-message
  105. (symbol-name symbol) candidates))))
  106. (deferred:nextc it
  107. (lambda (text)
  108. (or text (ycmd-eldoc--get-type))))
  109. (deferred:nextc it
  110. (lambda (text)
  111. (when text
  112. (setq text (ycmd--fontify-code text))
  113. (ycmd-eldoc--cache-store symbol text))))))))
  114. (defun ycmd-eldoc--cache-store (symbol text)
  115. "Store SYMBOL and TEXT to `ycmd-eldoc--cache'."
  116. (aset ycmd-eldoc--cache 0 symbol)
  117. ;; Store text only if we have a symbol for lookup
  118. (aset ycmd-eldoc--cache 1 (and symbol text))
  119. text)
  120. ;; Source: https://github.com/racer-rust/emacs-racer/blob/master/racer.el
  121. (defun ycmd-eldoc--goto-func-name ()
  122. "If point is inside a function call, move to the function name.
  123. foo(bar, |baz); -> foo|(bar, baz);"
  124. (let ((last-paren-pos (nth 1 (syntax-ppss)))
  125. (start-pos (point)))
  126. (when last-paren-pos
  127. ;; Move to just before the last paren.
  128. (goto-char last-paren-pos)
  129. ;; If we're inside a round paren, we're inside a function call.
  130. (unless (looking-at "(")
  131. ;; Otherwise, return to our start position, as point may have been on a
  132. ;; function already:
  133. ;; foo|(bar, baz);
  134. (goto-char start-pos)))))
  135. (defun ycmd-eldoc--generate-message (symbol result)
  136. "Generate eldoc message for SYMBOL from RESULT."
  137. (-when-let* ((filtered-list
  138. (--filter
  139. (let-alist it
  140. (and (s-equals? .insertion_text symbol)
  141. (or (not .extra_menu_info)
  142. (not (-contains?
  143. '("[ID]" "[File]" "[Dir]" "[File&Dir]")
  144. .extra_menu_info)))))
  145. result))
  146. (item (car filtered-list))
  147. (msg (or (cdr (assq 'detailed_info item))
  148. (cdr (assq 'extra_menu_info item)))))
  149. (unless (s-blank? msg)
  150. (car (s-split-up-to "\n" msg 1)))))
  151. (defun ycmd-eldoc--get-type ()
  152. "Get type at current position."
  153. (when ycmd-eldoc--cached-get-type-command
  154. (deferred:$
  155. (ycmd-eldoc--get-type-command-deferred)
  156. (deferred:nextc it
  157. (lambda (cmd)
  158. (when cmd
  159. (ycmd-with-handled-server-exceptions (ycmd--command-request cmd)
  160. (pcase-let ((`(,msg . ,is-type-p) (ycmd--get-message response)))
  161. (when is-type-p msg)))))))))
  162. (defun ycmd-eldoc--get-type-command-deferred ()
  163. "Return a deferred object with the chached GetType command.
  164. REQUEST-DATA is plist returned from `ycmd--get-request-data'."
  165. (if (eq ycmd-eldoc--cached-get-type-command 'none)
  166. (ycmd-with-handled-server-exceptions
  167. (ycmd--request (make-ycmd-request-data
  168. :handler "defined_subcommands"))
  169. (setq ycmd-eldoc--cached-get-type-command
  170. ;; If GetTypeImprecise exists, use it in favor of GetType
  171. ;; because it doesn't reparse the file
  172. (car (-intersection '("GetTypeImprecise" "GetType")
  173. response))))
  174. (deferred:next nil ycmd-eldoc--cached-get-type-command)))
  175. ;;;###autoload
  176. (defun ycmd-eldoc-setup ()
  177. "Setup eldoc for `ycmd-mode'."
  178. (interactive)
  179. (ycmd-eldoc-mode +1))
  180. (make-obsolete 'ycmd-eldoc-setup 'ycmd-eldoc-mode "0.2")
  181. (defun ycmd-eldoc--teardown ()
  182. "Reset `ycmd-eldoc--cache'."
  183. (ycmd-eldoc--cache-store nil nil)
  184. (setq ycmd-eldoc--cached-get-type-command 'none))
  185. ;;;###autoload
  186. (define-minor-mode ycmd-eldoc-mode
  187. "Toggle ycmd eldoc mode."
  188. :lighter ""
  189. (cond
  190. (ycmd-eldoc-mode
  191. ;; For emacs < 25.1 where `eldoc-documentation-function' defaults to
  192. ;; nil. See also https://github.com/abingham/emacs-ycmd/issues/409
  193. (or eldoc-documentation-function
  194. (setq-local eldoc-documentation-function #'ignore))
  195. (add-function :before-until (local 'eldoc-documentation-function)
  196. #'ycmd-eldoc--documentation-function)
  197. (eldoc-mode +1)
  198. (add-hook 'ycmd-after-teardown-hook
  199. #'ycmd-eldoc--teardown nil 'local))
  200. (t
  201. (eldoc-mode -1)
  202. (remove-function (local 'eldoc-documentation-function)
  203. #'ycmd-eldoc--documentation-function)
  204. (remove-hook 'ycmd-after-teardown-hook
  205. #'ycmd-eldoc--teardown 'local)
  206. (ycmd-eldoc--teardown))))
  207. (provide 'ycmd-eldoc)
  208. ;;; ycmd-eldoc.el ends here