company-tern.el 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. ;;; company-tern.el --- Tern backend for company-mode -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2013-2016 by Artem Malyshev
  3. ;; Author: Artem Malyshev <proofit404@gmail.com>
  4. ;; Maintainer: Matthew Kocubinski <mkocubinski@gmail.com>
  5. ;; URL: https://github.com/kocubinski/company-tern
  6. ;; Version: 0.3.0
  7. ;; Package-Requires: ((company "0.8.0") (tern "0.0.1") (dash "2.8.0") (dash-functional "2.8.0") (s "1.9.0") (cl-lib "0.5.0"))
  8. ;; This program is free software; you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation, either version 3 of the License, or
  11. ;; (at your option) any later version.
  12. ;; This program is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;; See the README for more details.
  20. ;;; Code:
  21. (require 'cl-lib)
  22. (require 'company)
  23. (require 'tern)
  24. (require 'dash)
  25. (require 'dash-functional)
  26. (require 's)
  27. (defgroup company-tern nil
  28. "Tern backend for company-mode"
  29. :group 'languages
  30. :prefix "company-tern-")
  31. (defcustom company-tern-property-marker " ○"
  32. "A string to indicate an object's own properties.
  33. This also can be nil to disable property markers."
  34. :type '(choice (string :tag "Property suffix")
  35. (const :tag "None" nil))
  36. :group 'company-tern)
  37. (defcustom company-tern-meta-as-single-line nil
  38. "Trim candidate type information to frame width?"
  39. :type 'boolean
  40. :group 'company-tern)
  41. (defun company-tern-prefix ()
  42. "Grab prefix for tern."
  43. (and tern-mode
  44. (not (company-in-string-or-comment))
  45. (or (company-grab-symbol-cons "\\." 1)
  46. 'stop)))
  47. (defun company-tern-candidates-query (prefix callback)
  48. "Retrieve PREFIX completion candidates from tern.
  49. Use CALLBACK function to display candidates."
  50. (tern-run-query
  51. (lambda (data)
  52. (funcall callback
  53. (company-tern-sort-by-depth
  54. (company-tern-format-candidates data))))
  55. '((type . "completions")
  56. (includeKeywords . t)
  57. (depths . t)
  58. (types . t)
  59. (docs . t))
  60. (point)))
  61. (defun company-tern-format-candidates (data)
  62. "Grab candidates with properties from tern DATA."
  63. (let ((completions (cdr (assq 'completions data)))
  64. (property-p (assq 'isProperty data)))
  65. (mapcar
  66. (lambda (completion)
  67. (let ((candidate (cdr (assq 'name completion))))
  68. (dolist (prop (push property-p completion))
  69. (put-text-property 0 1 (car prop) (cdr prop) candidate))
  70. candidate))
  71. completions)))
  72. (defun company-tern-sort-by-depth (candidates)
  73. "Sort CANDIDATES list by completion depth."
  74. (-sort (-on '< 'company-tern-depth) candidates))
  75. (defun company-tern-depth (candidate)
  76. "Return depth attribute for CANDIDATE."
  77. (get-text-property 0 'depth candidate))
  78. (defun company-tern-property-p (candidate)
  79. "Return t if CANDIDATE is object own property."
  80. (and (null (eq json-false (get-text-property 0 'isProperty candidate)))
  81. (eq 0 (company-tern-depth candidate))))
  82. (defun company-tern-keyword-p (candidate)
  83. "Return t if CANDIDATE is a keyword."
  84. (get-text-property 0 'isKeyword candidate))
  85. (defun company-tern-function-p (candidate)
  86. "Return t if CANDIDATE is a function."
  87. (--when-let (get-text-property 0 'type candidate)
  88. (s-starts-with? "fn(" it)))
  89. (defun company-tern-doc (candidate)
  90. "Return documentation buffer for CANDIDATE."
  91. (--when-let (get-text-property 0 'doc candidate)
  92. (company-doc-buffer it)))
  93. (defun company-tern-meta (candidate)
  94. "Return short documentation string for chosen CANDIDATE."
  95. (--when-let (get-text-property 0 'type candidate)
  96. (if company-tern-meta-as-single-line
  97. (s-left (frame-width) it)
  98. it)))
  99. (defun company-tern-annotation (candidate)
  100. "Return type annotation for chosen CANDIDATE."
  101. (--when-let (company-tern-get-type candidate)
  102. (concat it (and (company-tern-property-p candidate)
  103. company-tern-property-marker))))
  104. (defun company-tern-get-type (candidate)
  105. "Analyze CANDIDATE type."
  106. (unless (company-tern-keyword-p candidate)
  107. (if (company-tern-function-p candidate)
  108. (company-tern-function-type candidate)
  109. (company-tern-variable-type candidate))))
  110. (defun company-tern-function-type (candidate)
  111. "Get CANDIDATE type as a function."
  112. (-when-let* ((type (get-text-property 0 'type candidate))
  113. (annot (if company-tooltip-align-annotations "fn(%s)" "(%s)")))
  114. (->> (list (cons 'type type))
  115. (tern-parse-function-type)
  116. (cadr)
  117. (--map (car it))
  118. (-interpose ", ")
  119. (apply 'concat)
  120. (format annot))))
  121. (defun company-tern-variable-type (candidate)
  122. "Get CANDIDATE type as a variable."
  123. (-when-let* ((type (get-text-property 0 'type candidate))
  124. (annot (if company-tooltip-align-annotations "%s" " -> %s")))
  125. (format annot type)))
  126. ;;;###autoload
  127. (defun company-tern (command &optional arg &rest _args)
  128. "Tern backend for `company-mode'.
  129. See `company-backends' for more info about COMMAND and ARG."
  130. (interactive (list 'interactive))
  131. (cl-case command
  132. (interactive (company-begin-backend 'company-tern))
  133. (prefix (company-tern-prefix))
  134. (annotation (company-tern-annotation arg))
  135. (meta (company-tern-meta arg))
  136. (doc-buffer (company-tern-doc arg))
  137. (ignore-case t)
  138. (sorted t)
  139. (candidates (cons :async
  140. (lambda (callback)
  141. (company-tern-candidates-query arg callback))))))
  142. (provide 'company-tern)
  143. ;;; company-tern.el ends here