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.

623 lines
26 KiB

5 years ago
  1. ;;; company-ycmd.el --- company-mode backend for ycmd -*- lexical-binding: t -*-
  2. ;;
  3. ;; Copyright (c) 2014-2017 Austin Bingham, Peter Vasil
  4. ;;
  5. ;; Authors: Austin Bingham <austin.bingham@gmail.com>
  6. ;; Peter Vasil <mail@petervasil.net>
  7. ;; version: 0.2
  8. ;; Package-Version: 20180520.1053
  9. ;; URL: https://github.com/abingham/emacs-ycmd
  10. ;; Package-Requires: ((ycmd "1.3") (company "0.9.3") (deferred "0.5.1") (s "1.11.0") (dash "2.13.0") (let-alist "1.0.5") (f "0.19.0"))
  11. ;;
  12. ;; This file is not part of GNU Emacs.
  13. ;;
  14. ;;; Commentary:
  15. ;;
  16. ;; Description:
  17. ;;
  18. ;; ycmd is a modular code-completion framework. It includes, for
  19. ;; example, completion for C/C++/ObjC and Python. This module supplies
  20. ;; a company-mode backend for these completions.
  21. ;;
  22. ;; For more details, see the project page at
  23. ;; https://github.com/abingham/emacs-ycmd.
  24. ;;
  25. ;; Installation:
  26. ;;
  27. ;; Copy this file to to some location in your emacs load path. Then add
  28. ;; "(require 'company-ycmd)" to your emacs initialization (.emacs,
  29. ;; init.el, or something).
  30. ;;
  31. ;; Example config:
  32. ;;
  33. ;; (require 'company-ycmd)
  34. ;;
  35. ;;; License:
  36. ;;
  37. ;; Permission is hereby granted, free of charge, to any person
  38. ;; obtaining a copy of this software and associated documentation
  39. ;; files (the "Software"), to deal in the Software without
  40. ;; restriction, including without limitation the rights to use, copy,
  41. ;; modify, merge, publish, distribute, sublicense, and/or sell copies
  42. ;; of the Software, and to permit persons to whom the Software is
  43. ;; furnished to do so, subject to the following conditions:
  44. ;;
  45. ;; The above copyright notice and this permission notice shall be
  46. ;; included in all copies or substantial portions of the Software.
  47. ;;
  48. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  49. ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  50. ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  51. ;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  52. ;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  53. ;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  54. ;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  55. ;; SOFTWARE.
  56. ;;; Code:
  57. (eval-when-compile
  58. (require 'let-alist))
  59. (require 'cl-lib)
  60. (require 'company)
  61. (require 'company-template)
  62. (require 'deferred)
  63. (require 'ycmd)
  64. (require 's)
  65. (require 'f)
  66. (require 'dash)
  67. (require 'rx)
  68. (defgroup company-ycmd nil
  69. "Company-mode completion backend for ycmd."
  70. :group 'company
  71. :group 'ycmd)
  72. (defcustom company-ycmd-insert-arguments t
  73. "When non-nil, insert function arguments as a template after completion.
  74. Only supported by modes in `company-ycmd--extended-features-modes'"
  75. :type 'boolean)
  76. (defcustom company-ycmd-enable-fuzzy-matching t
  77. "When non-nil, use fuzzy matching for completion candidates.
  78. Setting this to nil enables the `company-mode' internal cache
  79. feature."
  80. :type 'boolean)
  81. (defcustom company-ycmd-show-completion-kind t
  82. "Show kind of completion entry."
  83. :type 'boolean)
  84. (defcustom company-ycmd-request-sync-timeout 0.05
  85. "Timeout for synchronous ycmd completion request.
  86. When 0, do not use synchronous completion request at all."
  87. :type 'number)
  88. (defconst company-ycmd--extended-features-modes
  89. '(c++-mode
  90. c-mode
  91. go-mode
  92. objc-mode
  93. rust-mode
  94. swift-mode
  95. python-mode
  96. js-mode
  97. typescript-mode)
  98. "Major modes which have extended features in `company-ycmd'.")
  99. (defun company-ycmd--extended-features-p ()
  100. "Check whether to use extended features."
  101. (memq major-mode company-ycmd--extended-features-modes))
  102. (defun company-ycmd--prefix-candidate-p (candidate prefix)
  103. "Return t if CANDIDATE string begins with PREFIX."
  104. (let ((insertion-text (cdr (assq 'insertion_text candidate))))
  105. (s-starts-with? prefix insertion-text t)))
  106. (defun company-ycmd--filename-completer-p (extra-info)
  107. "Check whether candidate's EXTRA-INFO indicates a filename completion."
  108. (-contains? '("[File]" "[Dir]" "[File&Dir]") extra-info))
  109. (defun company-ycmd--identifier-completer-p (extra-info)
  110. "Check if candidate's EXTRA-INFO indicates a identifier completion."
  111. (s-equals? "[ID]" extra-info))
  112. (defmacro company-ycmd--with-destructured-candidate (candidate &rest body)
  113. (declare (indent 1) (debug t))
  114. `(let-alist ,candidate
  115. (if (or (company-ycmd--identifier-completer-p .extra_menu_info)
  116. (company-ycmd--filename-completer-p .extra_menu_info))
  117. (propertize .insertion_text 'return_type .extra_menu_info)
  118. ,@body)))
  119. (defun company-ycmd--extract-params-clang (function-signature)
  120. "Extract parameters from FUNCTION-SIGNATURE if possible."
  121. (let ((params (company-ycmd--extract-params-clang-1
  122. function-signature)))
  123. (if (not (and params (string-prefix-p "(*)" params)))
  124. params
  125. (with-temp-buffer
  126. (insert params)
  127. (search-backward ")")
  128. (let ((pt (1+ (point))))
  129. (re-search-forward ".\\_>" nil t)
  130. (delete-region pt (point)))
  131. (buffer-string)))))
  132. (defun company-ycmd--extract-params-clang-1 (function-signature)
  133. "Extract parameters from FUNCTION-SIGNATURE if possible."
  134. (cond
  135. ((null function-signature) nil)
  136. ((string-match "[^:]:[^:]" function-signature)
  137. (substring function-signature (1+ (match-beginning 0))))
  138. ((string-match "\\((.*)[ a-z]*\\'\\)" function-signature)
  139. (let ((paren (match-beginning 1)))
  140. (if (not (and (eq (aref function-signature (1- paren)) ?>)
  141. (s-contains?
  142. "<" (substring function-signature 0 (1- paren)))))
  143. (match-string 1 function-signature)
  144. (with-temp-buffer
  145. (insert function-signature)
  146. (goto-char paren)
  147. (substring function-signature (1- (search-backward "<")))))))))
  148. (defun company-ycmd--convert-kind-clang (kind)
  149. "Convert KIND string for display."
  150. (pcase kind
  151. ("STRUCT" "struct")
  152. ("CLASS" "class")
  153. ("ENUM" "enum")
  154. ("TYPE" "type")
  155. ("MEMBER" "member")
  156. ("FUNCTION" "fn")
  157. ("VARIABLE" "var")
  158. ("MACRO" "macro")
  159. ("PARAMETER" "parameter")
  160. ("NAMESPACE" "namespace")))
  161. (defun company-ycmd--construct-candidate-clang (candidate)
  162. "Construct a completion string(s) from a CANDIDATE for cpp file-types.
  163. Returns a list with one candidate or multiple candidates for
  164. overloaded functions."
  165. (company-ycmd--with-destructured-candidate candidate
  166. (let* ((overloads (and company-ycmd-insert-arguments
  167. (stringp .detailed_info)
  168. (s-split "\n" .detailed_info t)))
  169. (items (or overloads (list .menu_text)))
  170. candidates)
  171. (when (eq major-mode 'objc-mode)
  172. (setq .insertion_text (s-chop-suffix ":" .insertion_text)))
  173. (dolist (it (delete-dups items) candidates)
  174. (let* ((meta (if overloads it .detailed_info))
  175. (kind (company-ycmd--convert-kind-clang .kind))
  176. (params (and (or (string= kind "fn") (string= kind "class"))
  177. (company-ycmd--extract-params-clang it)))
  178. (return-type (or (and overloads
  179. (let ((case-fold-search nil))
  180. (and (string-match
  181. (concat "\\(.*\\) [^ ]*"
  182. (regexp-quote .insertion_text))
  183. it)
  184. (match-string 1 it))))
  185. .extra_menu_info))
  186. (doc .extra_data.doc_string))
  187. (setq candidates
  188. (cons (propertize .insertion_text 'return_type return-type
  189. 'meta meta 'kind kind 'doc doc 'params params)
  190. candidates)))))))
  191. (defun company-ycmd--construct-candidate-go (candidate)
  192. "Construct completion string from a CANDIDATE for go file-types."
  193. (company-ycmd--with-destructured-candidate candidate
  194. (let* ((is-func (and .extra_menu_info
  195. (string-prefix-p "func" .extra_menu_info)))
  196. (meta (and .kind .menu_text .extra_menu_info
  197. (concat .kind " " .menu_text
  198. (if is-func
  199. (substring .extra_menu_info 4 nil)
  200. (concat " " .extra_menu_info)))))
  201. (return-type (and .extra_menu_info
  202. (string-match "^func(.*) \\(.*\\)" .extra_menu_info)
  203. (match-string 1 .extra_menu_info)))
  204. (params (and .extra_menu_info
  205. (or (string-match "^func\\((.*)\\) .*" .extra_menu_info)
  206. (string-match "^func\\((.*)\\)\\'" .extra_menu_info))
  207. (match-string 1 .extra_menu_info)))
  208. (kind (if (and .extra_menu_info (not is-func))
  209. (concat .kind ": " .extra_menu_info)
  210. .kind)))
  211. (propertize .insertion_text 'return_type return-type
  212. 'meta meta 'kind kind 'params params))))
  213. (defun company-ycmd--remove-self-from-function-args (args)
  214. "Remove function argument `self' from ARGS string."
  215. (if (s-contains? "self" args)
  216. (when (string-match "(\\(.*\\))" args)
  217. (->> (s-split "," (match-string 1 args) t)
  218. (cl-remove-if (lambda (s) (string-match-p "self" s)))
  219. (s-join ",")
  220. (s-trim-left)
  221. (s-prepend (substring args 0 (match-beginning 1)))
  222. (s-append (substring args (match-end 1)))))
  223. args))
  224. (defun company-ycmd--remove-template-args-from-function-args (args)
  225. "Remove template arguments from ARGS string."
  226. (if (s-starts-with? "<" args)
  227. (substring args (+ 1 (s-index-of ">" args)))
  228. args))
  229. (defun company-ycmd--extract-params-python (function-sig function-name)
  230. "Extract function arguments from FUNCTION-SIG.
  231. Use FUNCTION-NAME as part of the regex to match arguments.
  232. Replace any newline characters with spaces."
  233. (when (and function-sig
  234. (string-match
  235. (concat (regexp-quote function-name)
  236. ;; Regex to match everything between parentheses, including
  237. ;; newline.
  238. ;; https://www.emacswiki.org/emacs/MultilineRegexp
  239. "\\(([\0-\377[:nonascii:]]*?)\\).*")
  240. function-sig))
  241. (s-replace "\n" " " (match-string 1 function-sig))))
  242. (defun company-ycmd--extract-meta-python (doc-string)
  243. "Extract string for meta usage from DOC-STRING.
  244. Remove newline characters in function arguments and replace them
  245. with spaces."
  246. (when doc-string
  247. (if (string-match "\n" doc-string)
  248. (let (meta)
  249. (setq meta (substring doc-string 0 (match-beginning 0)))
  250. (while (and (string-match-p "(" meta)
  251. (not (string-match-p ")" meta)))
  252. (if (string-match "\n" doc-string (match-end 0))
  253. (setq meta (substring doc-string 0 (match-beginning 0)))
  254. (setq meta doc-string)))
  255. (s-replace "\n" " " meta))
  256. doc-string)))
  257. (defun company-ycmd--construct-candidate-python (candidate)
  258. "Construct completion string from a CANDIDATE for python file-types."
  259. (company-ycmd--with-destructured-candidate candidate
  260. (let* ((kind (s-replace "\n" " " .extra_menu_info))
  261. (params (and (s-prefix-p "def" kind)
  262. (company-ycmd--extract-params-python
  263. .detailed_info .insertion_text)))
  264. (meta (company-ycmd--extract-meta-python .detailed_info))
  265. (filepath .extra_data.location.filepath)
  266. (line-num .extra_data.location.line_num))
  267. (propertize .insertion_text 'meta meta 'doc .detailed_info 'kind kind
  268. 'params params 'filepath filepath 'line_num line-num))))
  269. ;; The next two function are taken from racer.el
  270. ;; https://github.com/racer-rust/emacs-racer
  271. (defun company-ycmd--file-and-parent (path)
  272. "Convert PATH /foo/bar/baz/q.txt to baz/q.txt."
  273. (let ((file (f-filename path))
  274. (parent (f-filename (f-parent path))))
  275. (f-join parent file)))
  276. (defun company-ycmd--trim-up-to (needle s)
  277. "Return content after the occurrence of NEEDLE in S."
  278. (-if-let (idx (s-index-of needle s))
  279. (substring s (+ idx (length needle)))
  280. s))
  281. (defun company-ycmd--construct-candidate-rust (candidate)
  282. "Construct completion string from CANDIDATE for rust file-types."
  283. (company-ycmd--with-destructured-candidate candidate
  284. (let* ((meta .extra_menu_info)
  285. (context (pcase .kind
  286. ("Module"
  287. (if (string= .insertion_text .extra_menu_info)
  288. ""
  289. (concat " " (company-ycmd--file-and-parent
  290. .extra_menu_info))))
  291. ("StructField"
  292. (concat " " .extra_menu_info))
  293. (_
  294. (->> .extra_menu_info
  295. (company-ycmd--trim-up-to .insertion_text)
  296. (s-chop-suffixes '(" {" "," ";"))))))
  297. (annotation (concat context
  298. (when (s-present? .kind)
  299. (format " [%s]" .kind))))
  300. (params (and (string= "Function" .kind)
  301. (if (string-match "\\(.*?\\) -> .*" context)
  302. (match-string 1 context)
  303. context)))
  304. (filepath .extra_data.location.filepath)
  305. (line-num .extra_data.location.line_num)
  306. (column-num .extra_data.location.column_num))
  307. (propertize .insertion_text 'meta meta 'kind .kind
  308. 'params params 'annotation annotation
  309. 'filepath filepath 'line_num line-num
  310. 'column_num column-num))))
  311. ;; The next two function are taken from company-sourcekit.el
  312. ;; https://github.com/nathankot/company-sourcekit
  313. (defun company-ycmd--normalize-source-text-sourcekit (sourcetext)
  314. "Make a more readable completion candidate out of one with
  315. placeholders inserted by SourceKit."
  316. (replace-regexp-in-string
  317. "<#T##\\(.*?\\)#>"
  318. (lambda (str)
  319. ;; <#T##Int#> - No label, argument only
  320. (save-match-data
  321. (string-match "<#T##\\(.*?\\)#>" str)
  322. (format "%s" (car (split-string (match-string 1 str) "#")))))
  323. sourcetext))
  324. (defun company-ycmd--build-yasnippet-sourcekit (sourcetext)
  325. "Build a yasnippet-compatible snippet from the given source
  326. text template generated by SourceKit."
  327. (replace-regexp-in-string
  328. "<#T##\\(.*?\\)#>"
  329. (lambda (str)
  330. ;; <#T##Int#> - No label, argument only
  331. (save-match-data
  332. (string-match "<#T##\\(.*?\\)#>" str)
  333. (format "${%s}" (car (split-string (match-string 1 str) "#")))))
  334. sourcetext))
  335. (defun company-ycmd--construct-candidate-swift (candidate)
  336. "Construct completion string from CANDIDATE for swift file-types."
  337. (company-ycmd--with-destructured-candidate candidate
  338. (propertize (company-ycmd--normalize-source-text-sourcekit .insertion_text)
  339. 'sourcetext .insertion_text)))
  340. (defun company-ycmd--construct-candidate-javascript (candidate)
  341. "Construct completion string from CANDIDATE for js file-types."
  342. (company-ycmd--with-destructured-candidate candidate
  343. (let* ((kind (or (and (string-match "^fn" .extra_menu_info)
  344. (match-string 0 .extra_menu_info))
  345. .extra_menu_info))
  346. (meta .extra_menu_info)
  347. (params (and .extra_menu_info
  348. (string-match "^fn\\((.*)\\).*" .extra_menu_info)
  349. (match-string 1 .extra_menu_info)))
  350. (return-type (and .extra_menu_info
  351. (string-match "^fn(.*) -> \\(.*\\)" .extra_menu_info)
  352. (match-string 1 .extra_menu_info)))
  353. (doc .detailed_info))
  354. (propertize .insertion_text 'meta meta 'params params
  355. 'return_type return-type 'kind kind 'doc doc))))
  356. (defun company-ycmd--construct-candidate-typescript (candidate)
  357. "Generic function to construct completion string from a CANDIDATE."
  358. (company-ycmd--with-destructured-candidate candidate
  359. (let* ((kind .kind)
  360. (meta (and .menu_text
  361. (if .extra_data
  362. (concat "(" .extra_data ") " .menu_text)
  363. (if (string-match (concat (regexp-quote .insertion_text)
  364. "\s*\\(.*\\)")
  365. .menu_text)
  366. (match-string 1 .menu_text)
  367. .menu_text))))
  368. (base-regexp (concat "^" (regexp-quote .insertion_text)
  369. "\s*(" (regexp-quote .kind) ") "
  370. ".*\." (regexp-quote .insertion_text)))
  371. (params (and .menu_text
  372. (string-match (concat base-regexp "\\((.*)\\):.*")
  373. .menu_text)
  374. (match-string 1 .menu_text)))
  375. (return-type (and .menu_text
  376. (string-match
  377. (concat base-regexp
  378. (and params (regexp-quote params))
  379. ": \\(.*\\)")
  380. .menu_text)
  381. (match-string 1 .menu_text))))
  382. (propertize .insertion_text 'kind kind 'meta meta
  383. 'params params 'return_type return-type))))
  384. (defun company-ycmd--construct-candidate-generic (candidate)
  385. "Generic function to construct completion string from a CANDIDATE."
  386. (company-ycmd--with-destructured-candidate candidate .insertion_text))
  387. (defun company-ycmd--construct-candidates (completions
  388. prefix
  389. start-col
  390. construct-candidate-fn)
  391. "Construct candidates list from COMPLETIONS.
  392. PREFIX is the prefix we calculated for doing the completion, and
  393. START-COL is the column on which ycmd indicates we should place
  394. the completion candidates. If START-COL differs from start column
  395. offset of PREFIX, we need to calculate the substring from PREFIX
  396. for that difference and prepend it to the insertion-text.
  397. CONSTRUCT-CANDIDATE-FN is a function to construct a completion
  398. candidate. See `company-ycmd--get-construct-candidate-fn'.
  399. When `company-ycmd-enable-fuzzy-matching' is nil, check if
  400. candidate starts with PREFIX, whether to include candidate in
  401. candidates list."
  402. (let* ((prefix-start-col (- (+ 1 (ycmd--column-in-bytes)) (length prefix)))
  403. (prefix-size (- start-col prefix-start-col))
  404. (prefix-diff (substring-no-properties prefix 0 prefix-size))
  405. (prefix-diff-p (s-present? prefix-diff))
  406. candidates)
  407. (dolist (candidate completions (nreverse candidates))
  408. (when prefix-diff-p
  409. (let ((it (cdr (assq 'insertion_text candidate))))
  410. (setf it (s-prepend prefix-diff it))))
  411. (when (or company-ycmd-enable-fuzzy-matching
  412. (company-ycmd--prefix-candidate-p candidate prefix))
  413. (let ((result (funcall construct-candidate-fn candidate)))
  414. (if (listp result)
  415. (setq candidates (append result candidates))
  416. (setq candidates (cons result candidates))))))))
  417. (defun company-ycmd--get-construct-candidate-fn ()
  418. "Return function to construct candidate(s) for current `major-mode'."
  419. (pcase (car-safe (ycmd-major-mode-to-file-types major-mode))
  420. ((or `"cpp" `"c" `"objc") 'company-ycmd--construct-candidate-clang)
  421. ("go" 'company-ycmd--construct-candidate-go)
  422. ("python" 'company-ycmd--construct-candidate-python)
  423. ("rust" 'company-ycmd--construct-candidate-rust)
  424. ("swift" 'company-ycmd--construct-candidate-swift)
  425. ("javascript" 'company-ycmd--construct-candidate-javascript)
  426. ("typescript" 'company-ycmd--construct-candidate-typescript)
  427. (_ 'company-ycmd--construct-candidate-generic)))
  428. (defun company-ycmd--get-candidates (completions prefix &optional cb)
  429. "Get candidates for COMPLETIONS and PREFIX.
  430. If CB is non-nil, call it with candidates."
  431. (let-alist completions
  432. (funcall
  433. (or cb 'identity)
  434. (company-ycmd--construct-candidates
  435. .completions prefix .completion_start_column
  436. (company-ycmd--get-construct-candidate-fn)))))
  437. (defun company-ycmd--get-candidates-deferred (prefix cb)
  438. "Get completion candidates with PREFIX and call CB deferred."
  439. (let ((request-window (selected-window))
  440. (request-point (point))
  441. (request-tick (buffer-chars-modified-tick)))
  442. (ycmd-with-handled-server-exceptions (deferred:try (ycmd-get-completions)
  443. :catch (lambda (_err) nil))
  444. :bind-current-buffer t
  445. (if (or (not (equal request-window (selected-window)))
  446. (with-current-buffer (window-buffer request-window)
  447. (or (not (equal request-buffer (current-buffer)))
  448. (not (equal request-point (point)))
  449. (not (equal request-tick (buffer-chars-modified-tick))))))
  450. (message "Skip ycmd completion response")
  451. (company-ycmd--get-candidates response prefix cb)))))
  452. (defun company-ycmd--meta (candidate)
  453. "Fetch the metadata text-property from a CANDIDATE string."
  454. (let ((meta (get-text-property 0 'meta candidate)))
  455. (if (stringp meta)
  456. (let ((meta-trimmed (s-trim meta)))
  457. (if (company-ycmd--extended-features-p)
  458. (ycmd--fontify-code meta-trimmed)
  459. meta-trimmed))
  460. meta)))
  461. (defun company-ycmd--annotation (candidate)
  462. "Fetch the annotation text-property from a CANDIDATE string."
  463. (-if-let (annotation (get-text-property 0 'annotation candidate))
  464. annotation
  465. (let ((kind (and company-ycmd-show-completion-kind
  466. (get-text-property 0 'kind candidate)))
  467. (return-type (get-text-property 0 'return_type candidate))
  468. (params (get-text-property 0 'params candidate)))
  469. (concat params
  470. (when (s-present? return-type)
  471. (s-prepend " -> " return-type))
  472. (when (s-present? kind)
  473. (format " [%s]" kind))))))
  474. (defconst company-ycmd--include-declaration
  475. (rx line-start "#" (zero-or-more blank) (or "include" "import")
  476. (one-or-more blank)
  477. (submatch (in "<\"") (zero-or-more (not (in ">\"")))))
  478. "Regular expression to find C/C++/ObjC include directives.")
  479. (defun company-ycmd--in-include ()
  480. "Check if text before point is an include statement."
  481. (looking-back company-ycmd--include-declaration
  482. (line-beginning-position)))
  483. (defun company-ycmd--prefix ()
  484. "Prefix-command handler for the company backend."
  485. (and ycmd-mode
  486. buffer-file-name
  487. (ycmd-running-p)
  488. (or (not (company-in-string-or-comment))
  489. (company-ycmd--in-include))
  490. (or (company-grab-symbol-cons "\\.\\|->\\|::\\|/" 2)
  491. 'stop)))
  492. (defun company-ycmd--candidates (prefix)
  493. "Candidates-command handler for the company backend for PREFIX."
  494. (let ((fetcher (cons :async
  495. (lambda (cb)
  496. (company-ycmd--get-candidates-deferred prefix cb)))))
  497. (if (> company-ycmd-request-sync-timeout 0)
  498. (let ((result (ycmd-deferred:sync!
  499. (ycmd-deferred:timeout company-ycmd-request-sync-timeout
  500. (funcall (cdr fetcher) nil)))))
  501. (if (eq result 'timeout) fetcher result))
  502. fetcher)))
  503. (defun company-ycmd--post-completion (candidate)
  504. "Insert function arguments after completion for CANDIDATE."
  505. (if (eq major-mode 'swift-mode)
  506. (company-ycmd--post-completion-swift candidate)
  507. (--when-let (and (company-ycmd--extended-features-p)
  508. company-ycmd-insert-arguments
  509. (get-text-property 0 'params candidate))
  510. (when (memq major-mode '(python-mode rust-mode))
  511. (setq it (company-ycmd--remove-self-from-function-args it))
  512. (when (eq major-mode 'rust-mode)
  513. (setq it (company-ycmd--remove-template-args-from-function-args it))))
  514. (insert it)
  515. (if (string-match "\\`:[^:]" it)
  516. (company-template-objc-templatify it)
  517. (company-template-c-like-templatify
  518. (concat candidate it))))))
  519. (declare-function yas-expand-snippet "yasnippet")
  520. (defun company-ycmd--post-completion-swift (candidate)
  521. "Insert function arguments after completion for CANDIDATE in
  522. swift mode."
  523. (let ((template (company-ycmd--build-yasnippet-sourcekit
  524. (get-text-property 0 'sourcetext candidate))))
  525. (yas-expand-snippet template (- (point) (length candidate)) (point))))
  526. (defun company-ycmd--doc-buffer (candidate)
  527. "Return buffer with docstring for CANDIDATE if it is available."
  528. (let ((doc (get-text-property 0 'doc candidate)))
  529. (when (s-present? doc)
  530. (company-doc-buffer doc))))
  531. (defun company-ycmd--location (candidate)
  532. "Return location for CANDIDATE."
  533. (-when-let* ((filepath (get-text-property 0 'filepath candidate))
  534. (line-num (get-text-property 0 'line_num candidate)))
  535. (cons filepath line-num)))
  536. (defun company-ycmd (command &optional arg &rest ignored)
  537. "The company-backend command handler for ycmd."
  538. (interactive (list 'interactive))
  539. (cl-case command
  540. (interactive (company-begin-backend 'company-ycmd))
  541. (prefix (company-ycmd--prefix))
  542. (candidates (company-ycmd--candidates arg))
  543. (meta (company-ycmd--meta arg))
  544. (annotation (company-ycmd--annotation arg))
  545. (no-cache company-ycmd-enable-fuzzy-matching)
  546. (sorted 't)
  547. (post-completion (company-ycmd--post-completion arg))
  548. (doc-buffer (company-ycmd--doc-buffer arg))
  549. (location (company-ycmd--location arg))))
  550. ;;;###autoload
  551. (defun company-ycmd-setup ()
  552. "Add company-ycmd to the front of company-backends."
  553. (add-to-list 'company-backends 'company-ycmd))
  554. (defun company-ycmd--init ()
  555. (unless (eq company-minimum-prefix-length
  556. ycmd-min-num-chars-for-completion)
  557. (setq-local company-minimum-prefix-length
  558. ycmd-min-num-chars-for-completion)))
  559. (add-hook 'ycmd-mode-hook #'company-ycmd--init)
  560. (provide 'company-ycmd)
  561. ;;; company-ycmd.el ends here
  562. ;; Local Variables:
  563. ;; indent-tabs-mode: nil
  564. ;; End: