Klimi's new dotfiles with stow.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

2478 wiersze
93 KiB

4 lat temu
  1. ;;; ycmd.el --- emacs bindings to the ycmd completion server -*- 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: 1.3-cvs
  8. ;; URL: https://github.com/abingham/emacs-ycmd
  9. ;; Package-Requires: ((emacs "24.4") (dash "2.13.0") (s "1.11.0") (deferred "0.5.1") (cl-lib "0.6.1") (let-alist "1.0.5") (request "0.3.0") (request-deferred "0.3.0") (pkg-info "0.6"))
  10. ;;
  11. ;; This file is not part of GNU Emacs.
  12. ;;
  13. ;;; Commentary:
  14. ;;
  15. ;; Description:
  16. ;;
  17. ;; ycmd is a modular server that provides completion for C/C++/ObjC
  18. ;; and Python, among other languages. This module provides an emacs
  19. ;; client for that server.
  20. ;;
  21. ;; ycmd is a bit peculiar in a few ways. First, communication with the
  22. ;; server uses HMAC to authenticate HTTP messages. The server is
  23. ;; started with an HMAC secret that the client uses to generate hashes
  24. ;; of the content it sends. Second, the server gets this HMAC
  25. ;; information (as well as other configuration information) from a
  26. ;; file that the server deletes after reading. So when the code in
  27. ;; this module starts a server, it has to create a file containing the
  28. ;; secret code. Since the server deletes this file, this code has to
  29. ;; create a new one for each server it starts. Hopefully by knowing
  30. ;; this, you'll be able to make more sense of some of what you see
  31. ;; below.
  32. ;;
  33. ;; For more details, see the project page at
  34. ;; https://github.com/abingham/emacs-ycmd.
  35. ;;
  36. ;; Installation:
  37. ;;
  38. ;; Copy this file to to some location in your emacs load path. Then add
  39. ;; "(require 'ycmd)" to your emacs initialization (.emacs,
  40. ;; init.el, or something).
  41. ;;
  42. ;; Example config:
  43. ;;
  44. ;; (require 'ycmd)
  45. ;; (ycmd-setup)
  46. ;;
  47. ;; Basic usage:
  48. ;;
  49. ;; First you'll want to configure a few things. If you've got a global
  50. ;; ycmd config file, you can specify that with `ycmd-global-config':
  51. ;;
  52. ;; (set-variable 'ycmd-global-config "/path/to/global_conf.py")
  53. ;;
  54. ;; Then you'll want to configure your "extra-config whitelist"
  55. ;; patterns. These patterns determine which extra-conf files will get
  56. ;; loaded automatically by ycmd. So, for example, if you want to make
  57. ;; sure that ycmd will automatically load all of the extra-conf files
  58. ;; underneath your "~/projects" directory, do this:
  59. ;;
  60. ;; (set-variable 'ycmd-extra-conf-whitelist '("~/projects/*"))
  61. ;;
  62. ;; Now, the first time you open a file for which ycmd can perform
  63. ;; completions, a ycmd server will be automatically started.
  64. ;;
  65. ;; When ycmd encounters an extra-config that's not on the white list,
  66. ;; it checks `ycmd-extra-conf-handler' to determine what to do. By
  67. ;; default this is set to `ask', in which case the user is asked
  68. ;; whether to load the file or ignore it. You can also set it to
  69. ;; `load', in which case all extra-confs are loaded (and you don't
  70. ;; really need to worry about `ycmd-extra-conf-whitelist'.) Or you can
  71. ;; set this to `ignore', in which case all extra-confs are
  72. ;; automatically ignored.
  73. ;;
  74. ;; Use `ycmd-get-completions' to get completions at some point in a
  75. ;; file. For example:
  76. ;;
  77. ;; (ycmd-get-completions buffer position)
  78. ;;
  79. ;; You can use `ycmd-display-completions' to toy around with completion
  80. ;; interactively and see the shape of the structures in use.
  81. ;;
  82. ;;; License:
  83. ;;
  84. ;; Permission is hereby granted, free of charge, to any person
  85. ;; obtaining a copy of this software and associated documentation
  86. ;; files (the "Software"), to deal in the Software without
  87. ;; restriction, including without limitation the rights to use, copy,
  88. ;; modify, merge, publish, distribute, sublicense, and/or sell copies
  89. ;; of the Software, and to permit persons to whom the Software is
  90. ;; furnished to do so, subject to the following conditions:
  91. ;;
  92. ;; The above copyright notice and this permission notice shall be
  93. ;; included in all copies or substantial portions of the Software.
  94. ;;
  95. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  96. ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  97. ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  98. ;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  99. ;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  100. ;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  101. ;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  102. ;; SOFTWARE.
  103. ;;; Code:
  104. (eval-when-compile
  105. (require 'cl-lib)
  106. (require 'let-alist))
  107. (require 'dash)
  108. (require 's)
  109. (require 'deferred)
  110. (require 'hmac-def)
  111. (require 'json)
  112. (require 'request)
  113. (require 'request-deferred)
  114. (require 'etags)
  115. (require 'easymenu)
  116. (require 'diff)
  117. (require 'diff-mode)
  118. (declare-function pkg-info-version-info "pkg-info" (package))
  119. (defgroup ycmd nil
  120. "a ycmd emacs client"
  121. :link '(url-link :tag "Github" "https://github.com/abingham/emacs-ycmd")
  122. :group 'tools
  123. :group 'programming)
  124. (defcustom ycmd-global-config nil
  125. "Path to global extra conf file."
  126. :type '(string))
  127. (defcustom ycmd-extra-conf-whitelist nil
  128. "List of glob expressions which match extra configs.
  129. Whitelisted configs are loaded without confirmation."
  130. :type '(repeat string))
  131. (defcustom ycmd-extra-conf-handler 'ask
  132. "What to do when an un-whitelisted extra config is encountered.
  133. Options are:
  134. `load'
  135. Automatically load unknown extra confs.
  136. `ignore'
  137. Ignore unknown extra confs and do not load them.
  138. `ask'
  139. Ask the user for each unknown extra conf."
  140. :type '(choice (const :tag "Load unknown extra confs" load)
  141. (const :tag "Ignore unknown extra confs" ignore)
  142. (const :tag "Ask the user" ask))
  143. :risky t)
  144. (defcustom ycmd-host "127.0.0.1"
  145. "The host on which the ycmd server is running."
  146. :type '(string))
  147. (defcustom ycmd-server-command nil
  148. "The ycmd server program command.
  149. The value is a list of arguments to run the ycmd server.
  150. Example value:
  151. \(set-variable 'ycmd-server-command (\"python\" \"/path/to/ycmd/package/\"))"
  152. :type '(repeat string))
  153. (defcustom ycmd-server-args '("--log=debug"
  154. "--keep_logfile"
  155. "--idle_suicide_seconds=10800")
  156. "Extra arguments to pass to the ycmd server."
  157. :type '(repeat string))
  158. (defcustom ycmd-server-port nil
  159. "The ycmd server port. If nil, use random port."
  160. :type '(number))
  161. (defcustom ycmd-file-parse-result-hook nil
  162. "Functions to run with file-parse results.
  163. Each function will be called with with the results returned from
  164. ycmd when it parses a file in response to
  165. /event_notification."
  166. :type 'hook
  167. :risky t)
  168. (defcustom ycmd-idle-change-delay 0.5
  169. "Number of seconds to wait after buffer modification before
  170. re-parsing the contents."
  171. :type '(number)
  172. :safe #'numberp)
  173. (defcustom ycmd-keepalive-period 600
  174. "Number of seconds between keepalive messages."
  175. :type '(number))
  176. (defcustom ycmd-startup-timeout 3
  177. "Number of seconds to wait for the server to start."
  178. :type '(number))
  179. (defcustom ycmd-delete-process-delay 3
  180. "Seconds to wait for the server to finish before killing the process."
  181. :type '(number))
  182. (defcustom ycmd-parse-conditions '(save new-line mode-enabled)
  183. "When ycmd should reparse the buffer.
  184. The variable is a list of events that may trigger parsing the
  185. buffer for new completion:
  186. `save'
  187. Set buffer-needs-parse flag after the buffer was saved.
  188. `new-line'
  189. Set buffer-needs-parse flag immediately after a new
  190. line was inserted into the buffer.
  191. `idle-change'
  192. Set buffer-needs-parse flag a short time after a
  193. buffer has changed. (See `ycmd-idle-change-delay')
  194. `mode-enabled'
  195. Set buffer-needs-parse flag after `ycmd-mode' has been
  196. enabled.
  197. `buffer-focus'
  198. Set buffer-needs-parse flag when an unparsed buffer gets
  199. focus.
  200. If nil, never set buffer-needs-parse flag. For a manual reparse,
  201. use `ycmd-parse-buffer'."
  202. :type '(set (const :tag "After the buffer was saved" save)
  203. (const :tag "After a new line was inserted" new-line)
  204. (const :tag "After a buffer was changed and idle" idle-change)
  205. (const :tag "After a `ycmd-mode' was enabled" mode-enabled)
  206. (const :tag "After an unparsed buffer gets focus" buffer-focus))
  207. :safe #'listp)
  208. (defcustom ycmd-default-tags-file-name "tags"
  209. "The default tags file name."
  210. :type 'string)
  211. (defcustom ycmd-force-semantic-completion nil
  212. "Whether to use always semantic completion."
  213. :type 'boolean)
  214. (defcustom ycmd-auto-trigger-semantic-completion t
  215. "If non-nil, semantic completion is turned off.
  216. Semantic completion is still available if
  217. `ycmd-force-semantic-completion' is non-nil."
  218. :type 'boolean)
  219. (defcustom ycmd-hide-url-status t
  220. "Whether to quash url status messages for ycmd requests."
  221. :type 'boolean)
  222. (defcustom ycmd-bypass-url-proxy-services t
  223. "Bypass proxies for local traffic with the ycmd server.
  224. If non-nil, bypass the variable `url-proxy-services' in
  225. `ycmd--request' by setting it to nil and add `no_proxy' to
  226. `process-environment' to bypass proxies when using `curl' as
  227. `request-backend' and for the ycmd process."
  228. :type 'boolean)
  229. (defcustom ycmd-tag-files nil
  230. "Whether to collect identifiers from tags file.
  231. nil
  232. Do not collect identifiers from tag files.
  233. `auto'
  234. Look up directory hierarchy for first found tags file with
  235. `ycmd-default-tags-file-name'.
  236. string
  237. A tags file name.
  238. list
  239. A list of tag file names."
  240. :type '(choice (const :tag "Don't use tag file." nil)
  241. (const :tag "Locate tags file automatically" auto)
  242. (string :tag "Tag file name")
  243. (repeat :tag "List of tag files"
  244. (string :tag "Tag file name")))
  245. :safe (lambda (obj)
  246. (or (symbolp obj)
  247. (stringp obj)
  248. (ycmd--string-list-p obj))))
  249. (defcustom ycmd-file-type-map
  250. '((c++-mode . ("cpp"))
  251. (c-mode . ("c"))
  252. (caml-mode . ("ocaml"))
  253. (csharp-mode . ("cs"))
  254. (d-mode . ("d"))
  255. (erlang-mode . ("erlang"))
  256. (emacs-lisp-mode . ("elisp"))
  257. (go-mode . ("go"))
  258. (java-mode . ("java"))
  259. (js-mode . ("javascript"))
  260. (js2-mode . ("javascript"))
  261. (lua-mode . ("lua"))
  262. (objc-mode . ("objc"))
  263. (perl-mode . ("perl"))
  264. (cperl-mode . ("perl"))
  265. (php-mode . ("php"))
  266. (python-mode . ("python"))
  267. (ruby-mode . ("ruby"))
  268. (rust-mode . ("rust"))
  269. (swift-mode . ("swift"))
  270. (scala-mode . ("scala"))
  271. (tuareg-mode . ("ocaml"))
  272. (typescript-mode . ("typescript")))
  273. "Mapping from major modes to ycmd file-type strings.
  274. Used to determine a) which major modes we support and b) how to
  275. describe them to ycmd."
  276. :type '(alist :key-type symbol :value-type (repeat string)))
  277. (defcustom ycmd-min-num-chars-for-completion 2
  278. "The minimum number of characters for identifier completion.
  279. It controls the number of characters the user needs to type
  280. before identifier-based completion suggestions are triggered.
  281. This option is NOT used for semantic completion.
  282. Setting this it to a high number like 99 effectively turns off
  283. the identifier completion engine and just leaves the semantic
  284. engine."
  285. :type 'integer)
  286. (defcustom ycmd-max-num-identifier-candidates 10
  287. "The maximum number of identifier completion results."
  288. :type 'integer)
  289. (defcustom ycmd-seed-identifiers-with-keywords nil
  290. "Whether to seed identifier database with keywords."
  291. :type 'boolean)
  292. (defcustom ycmd-get-keywords-function 'ycmd--get-keywords-from-alist
  293. "Function to get keywords for current mode."
  294. :type 'symbol)
  295. (defcustom ycmd-gocode-binary-path nil
  296. "Gocode binary path."
  297. :type 'string)
  298. (defcustom ycmd-godef-binary-path nil
  299. "Godef binary path."
  300. :type 'string)
  301. (defcustom ycmd-rust-src-path nil
  302. "Rust source path."
  303. :type 'string)
  304. (defcustom ycmd-swift-src-path nil
  305. "Swift source path."
  306. :type 'string)
  307. (defcustom ycmd-racerd-binary-path nil
  308. "Racerd binary path."
  309. :type 'string)
  310. (defcustom ycmd-python-binary-path nil
  311. "Python binary path."
  312. :type 'string)
  313. (defcustom ycmd-global-modes t
  314. "Modes for which `ycmd-mode' is turned on by `global-ycmd-mode'.
  315. If t, ycmd mode is turned on for all major modes in
  316. `ycmd-file-type-map'. If set to all, ycmd mode is turned on
  317. for all major-modes. If a list, ycmd mode is turned on for all
  318. `major-mode' symbols in that list. If the `car' of the list is
  319. `not', ycmd mode is turned on for all `major-mode' symbols _not_
  320. in that list. If nil, ycmd mode is never turned on by
  321. `global-ycmd-mode'."
  322. :type '(choice (const :tag "none" nil)
  323. (const :tag "member in `ycmd-file-type-map'" t)
  324. (const :tag "all" all)
  325. (set :menu-tag "mode specific" :tag "modes"
  326. :value (not)
  327. (const :tag "Except" not)
  328. (repeat :inline t (symbol :tag "mode")))))
  329. (defcustom ycmd-confirm-fixit t
  330. "Whether to confirm when applying fixit on line."
  331. :type 'boolean)
  332. (defcustom ycmd-after-exception-hook nil
  333. "Function to run if server request resulted in exception.
  334. This hook is run whenever an exception is thrown after a ycmd
  335. server request. Four arguments are passed to the function, a
  336. string with the type of request that triggerd the exception, the
  337. buffer and the point at the time of the request and the server
  338. response structure which looks like this:
  339. ((exception
  340. (TYPE . \"RuntimeError\"))
  341. (traceback . \"long traceback string\")
  342. (message . \"Can't jump to definition.\"))
  343. This variable is a normal hook. See Info node `(elisp)Hooks'."
  344. :type 'hook
  345. :risky t)
  346. (defcustom ycmd-after-teardown-hook nil
  347. "Functions to run after execution of `ycmd--teardown'.
  348. This variable is a normal hook. See Info node `(elisp)Hooks'."
  349. :type 'hook
  350. :risky t)
  351. (defcustom ycmd-mode-line-prefix "ycmd"
  352. "Base mode line lighter for ycmd."
  353. :type 'string
  354. :package-version '(ycmd . "1.3"))
  355. (defcustom ycmd-completing-read-function #'completing-read
  356. "Function to read from minibuffer with completion.
  357. The function must be compatible to the built-in `completing-read'
  358. function."
  359. :type '(choice (const :tag "Default" completing-read)
  360. (const :tag "IDO" ido-completing-read)
  361. (function :tag "Custom function"))
  362. :risky t
  363. :package-version '(ycmd . "1.2"))
  364. (defconst ycmd--file-types-with-diagnostics
  365. '("c"
  366. "cpp"
  367. "objc"
  368. "objcpp"
  369. "cs"
  370. "typescript")
  371. "A list of ycmd file type strings which support semantic diagnostics.")
  372. (defvar ycmd-keywords-alist
  373. '((c++-mode
  374. "alignas" "alignof" "and" "and_eq" "asm" "auto" "bitand" "bitor" "bool"
  375. "break" "case" "catch" "char" "char16_t" "char32_t" "class" "compl"
  376. "concept" "const" "const_cast" "constexpr" "continue" "decltype" "default"
  377. "define" "defined" "delete" "do" "double" "dynamic_cast" "elif" "else"
  378. "endif" "enum" "error" "explicit" "export" "extern" "false" "final" "float"
  379. "for" "friend" "goto" "if" "ifdef" "ifndef" "include" "inline" "int" "line"
  380. "long" "mutable" "namespace" "new" "noexcept" "not" "not_eq" "nullptr"
  381. "operator" "or" "or_eq" "override" "pragma" "_Pragma" "private" "protected"
  382. "public" "register" "reinterpret_cast" "requires" "return" "short" "signed"
  383. "sizeof" "static" "static_assert" "static_cast" "struct" "switch"
  384. "template" "this" "thread_local" "throw" "true" "try" "typedef" "typeid"
  385. "typename" "union" "unsigned" "using" "virtual" "void" "volatile" "wchar_t"
  386. "while" "xor" "xor_eq")
  387. (c-mode
  388. "auto" "_Alignas" "_Alignof" "_Atomic" "_Bool" "break" "case" "char"
  389. "_Complex" "const" "continue" "default" "define" "defined" "do" "double"
  390. "elif" "else" "endif" "enum" "error" "extern" "float" "for" "goto"
  391. "_Generic" "if" "ifdef" "ifndef" "_Imaginary" "include" "inline" "int"
  392. "line" "long" "_Noreturn" "pragma" "register" "restrict" "return" "short"
  393. "signed" "sizeof" "static" "struct" "switch" "_Static_assert" "typedef"
  394. "_Thread_local" "undef" "union" "unsigned" "void" "volatile" "while")
  395. (go-mode
  396. "break" "case" "chan" "const" "continue" "default" "defer" "else"
  397. "fallthrough" "for" "func" "go" "goto" "if" "import" "interface" "map"
  398. "package" "range" "return" "select" "struct" "switch" "type" "var")
  399. (lua-mode
  400. "and" "break" "do" "else" "elseif" "end" "false" "for" "function" "if" "in"
  401. "local" "nil" "not" "or" "repeat" "return" "then" "true" "until" "while")
  402. (python-mode
  403. "ArithmeticError" "AssertionError" "AttributeError" "BaseException"
  404. "BufferError" "BytesWarning" "DeprecationWarning" "EOFError" "Ellipsis"
  405. "EnvironmentError" "Exception" "False" "FloatingPointError" "FutureWarning"
  406. "GeneratorExit" "IOError" "ImportError" "ImportWarning" "IndentationError"
  407. "IndexError" "KeyError" "KeyboardInterrupt" "LookupError" "MemoryError"
  408. "NameError" "None" "NotImplemented" "NotImplementedError" "OSError"
  409. "OverflowError" "PendingDeprecationWarning" "ReferenceError" "RuntimeError"
  410. "RuntimeWarning" "StandardError" "StopIteration" "SyntaxError"
  411. "SyntaxWarning" "SystemError" "SystemExit" "TabError" "True" "TypeError"
  412. "UnboundLocalError" "UnicodeDecodeError" "UnicodeEncodeError"
  413. "UnicodeError" "UnicodeTranslateError" "UnicodeWarning" "UserWarning"
  414. "ValueError" "Warning" "ZeroDivisionError" "__builtins__" "__debug__"
  415. "__doc__" "__file__" "__future__" "__import__" "__init__" "__main__"
  416. "__name__" "__package__" "_dummy_thread" "_thread" "abc" "abs" "aifc" "all"
  417. "and" "any" "apply" "argparse" "array" "as" "assert" "ast" "asynchat"
  418. "asyncio" "asyncore" "atexit" "audioop" "base64" "basestring" "bdb" "bin"
  419. "binascii" "binhex" "bisect" "bool" "break" "buffer" "builtins" "bytearray"
  420. "bytes" "bz2" "calendar" "callable" "cgi" "cgitb" "chr" "chuck" "class"
  421. "classmethod" "cmath" "cmd" "cmp" "code" "codecs" "codeop" "coerce"
  422. "collections" "colorsys" "compile" "compileall" "complex" "concurrent"
  423. "configparser" "contextlib" "continue" "copy" "copyreg" "copyright"
  424. "credits" "crypt" "csv" "ctypes" "curses" "datetime" "dbm" "decimal" "def"
  425. "del" "delattr" "dict" "difflib" "dir" "dis" "distutils" "divmod" "doctest"
  426. "dummy_threading" "elif" "else" "email" "enumerate" "ensurepip" "enum"
  427. "errno" "eval" "except" "exec" "execfile" "exit" "faulthandler" "fcntl"
  428. "file" "filecmp" "fileinput" "filter" "finally" "float" "fnmatch" "for"
  429. "format" "formatter" "fpectl" "fractions" "from" "frozenset" "ftplib"
  430. "functools" "gc" "getattr" "getopt" "getpass" "gettext" "glob" "global"
  431. "globals" "grp" "gzip" "hasattr" "hash" "hashlib" "heapq" "help" "hex"
  432. "hmac" "html" "http" "id" "if" "imghdr" "imp" "impalib" "import"
  433. "importlib" "in" "input" "inspect" "int" "intern" "io" "ipaddress" "is"
  434. "isinstance" "issubclass" "iter" "itertools" "json" "keyword" "lambda"
  435. "len" "license" "linecache" "list" "locale" "locals" "logging" "long"
  436. "lzma" "macpath" "mailbox" "mailcap" "map" "marshal" "math" "max"
  437. "memoryview" "mimetypes" "min" "mmap" "modulefinder" "msilib" "msvcrt"
  438. "multiprocessing" "netrc" "next" "nis" "nntplib" "not" "numbers" "object"
  439. "oct" "open" "operator" "optparse" "or" "ord" "os" "ossaudiodev" "parser"
  440. "pass" "pathlib" "pdb" "pickle" "pickletools" "pipes" "pkgutil" "platform"
  441. "plistlib" "poplib" "posix" "pow" "pprint" "print" "profile" "property"
  442. "pty" "pwd" "py_compiler" "pyclbr" "pydoc" "queue" "quit" "quopri" "raise"
  443. "random" "range" "raw_input" "re" "readline" "reduce" "reload" "repr"
  444. "reprlib" "resource" "return" "reversed" "rlcompleter" "round" "runpy"
  445. "sched" "select" "selectors" "self" "set" "setattr" "shelve" "shlex"
  446. "shutil" "signal" "site" "slice" "smtpd" "smtplib" "sndhdr" "socket"
  447. "socketserver" "sorted" "spwd" "sqlite3" "ssl" "stat" "staticmethod"
  448. "statistics" "str" "string" "stringprep" "struct" "subprocess" "sum"
  449. "sunau" "super" "symbol" "symtable" "sys" "sysconfig" "syslog" "tabnanny"
  450. "tarfile" "telnetlib" "tempfile" "termios" "test" "textwrap" "threading"
  451. "time" "timeit" "tkinter" "token" "tokenize" "trace" "traceback"
  452. "tracemalloc" "try" "tty" "tuple" "turtle" "type" "types" "unichr"
  453. "unicode" "unicodedata" "unittest" "urllib" "uu" "uuid" "vars" "venv"
  454. "warnings" "wave" "weakref" "webbrowser" "while" "winsound" "winreg" "with"
  455. "wsgiref" "xdrlib" "xml" "xmlrpc" "xrange" "yield" "zip" "zipfile" "zipimport"
  456. "zlib")
  457. (rust-mode
  458. "Self"
  459. "as" "box" "break" "const" "continue" "crate" "else" "enum" "extern"
  460. "false" "fn" "for" "if" "impl" "in" "let" "loop" "macro" "match" "mod"
  461. "move" "mut" "pub" "ref" "return" "self" "static" "struct" "super"
  462. "trait" "true" "type" "unsafe" "use" "where" "while")
  463. (swift-mode
  464. "true" "false" "nil" "#available" "#colorLiteral" "#column" "#else"
  465. "#elseif" "#endif" "#fileLiteral" "#file" "#function" "#if" "#imageLiteral"
  466. "#keypath" "#line" "#selector" "#sourceLocation" "associatedtype" "class"
  467. "deinit" "enum" "extension" "fileprivate" "func" "import" "init" "inout"
  468. "internal" "let" "open" "operator" "private" "protocol" "public" "static"
  469. "struct" "subscript" "typealias" "var" "break" "case" "continue" "default"
  470. "defer" "do" "else" "fallthrough" "for" "guard" "if" "in" "repeat" "return"
  471. "switch" "where" "while" "as" "catch" "dynamicType" "is" "rethrows" "super"
  472. "self" "Self" "throws" "throw" "try" "Protocol" "Type" "and" "assignment"
  473. "associativity" "convenience" "didSet" "dynamic" "final" "get" "higherThan"
  474. "indirect" "infix" "lazy" "left" "lowerThan" "mutating" "none"
  475. "nonmutating" "optional" "override" "postfix" "precedence"
  476. "precedencegroup" "prefix" "required" "right" "set" "unowned" "weak"
  477. "willSet"))
  478. "Alist mapping major-modes to keywords for.
  479. Keywords source: https://github.com/auto-complete/auto-complete/tree/master/dict
  480. and `company-keywords'.")
  481. (defvar ycmd--server-actual-port nil
  482. "The actual port being used by the ycmd server.
  483. This is set based on the value of `ycmd-server-port' if set, or
  484. the value from the output of the server itself.")
  485. (defvar ycmd--hmac-secret nil
  486. "This is populated with the hmac secret of the current connection.
  487. Users should never need to modify this, hence the defconst. It is
  488. not, however, treated as a constant by this code. This value gets
  489. set in ycmd-open.")
  490. (defconst ycmd--server-process-name "ycmd-server"
  491. "The Emacs name of the server process.
  492. This is used by functions like `start-process', `get-process'
  493. and `delete-process'.")
  494. (defvar-local ycmd--notification-timer nil
  495. "Timer for notifying ycmd server to do work, e.g. parsing files.")
  496. (defvar ycmd--keepalive-timer nil
  497. "Timer for sending keepalive messages to the server.")
  498. (defvar ycmd--on-focus-timer nil
  499. "Timer for deferring ycmd server notification to parse a buffer.")
  500. (defvar ycmd--server-timeout-timer nil)
  501. (defconst ycmd--server-buffer-name "*ycmd-server*"
  502. "Name of the ycmd server buffer.")
  503. (defvar-local ycmd--last-status-change 'unparsed
  504. "The last status of the current buffer.")
  505. (defvar-local ycmd--last-modified-tick nil
  506. "The BUFFER's last FileReadyToParse tick counter.")
  507. (defvar-local ycmd--buffer-visit-flag nil)
  508. (defvar ycmd--available-completers (make-hash-table :test 'eq))
  509. (defvar ycmd--process-environment nil)
  510. (defvar ycmd--mode-keywords-loaded nil
  511. "List of modes for which keywords have been loaded.")
  512. (defconst ycmd-hooks-alist
  513. '((after-save-hook . ycmd--on-save)
  514. (after-change-functions . ycmd--on-change)
  515. (window-configuration-change-hook . ycmd--on-window-configuration-change)
  516. (kill-buffer-hook . ycmd--on-close-buffer)
  517. (before-revert-hook . ycmd--teardown)
  518. (post-command-hook . ycmd--perform-deferred-parse))
  519. "Hooks which ycmd hooks in.")
  520. (add-hook 'kill-emacs-hook 'ycmd-close)
  521. (defvar ycmd-command-map
  522. (let ((map (make-sparse-keymap)))
  523. (define-key map "p" 'ycmd-parse-buffer)
  524. (define-key map "o" 'ycmd-open)
  525. (define-key map "c" 'ycmd-close)
  526. (define-key map "." 'ycmd-goto)
  527. (define-key map "gi" 'ycmd-goto-include)
  528. (define-key map "gd" 'ycmd-goto-definition)
  529. (define-key map "gD" 'ycmd-goto-declaration)
  530. (define-key map "gm" 'ycmd-goto-implementation)
  531. (define-key map "gp" 'ycmd-goto-imprecise)
  532. (define-key map "gr" 'ycmd-goto-references)
  533. (define-key map "gt" 'ycmd-goto-type)
  534. (define-key map "s" 'ycmd-toggle-force-semantic-completion)
  535. (define-key map "v" 'ycmd-show-debug-info)
  536. (define-key map "V" 'ycmd-version)
  537. (define-key map "d" 'ycmd-show-documentation)
  538. (define-key map "C" 'ycmd-clear-compilation-flag-cache)
  539. (define-key map "O" 'ycmd-restart-semantic-server)
  540. (define-key map "t" 'ycmd-get-type)
  541. (define-key map "T" 'ycmd-get-parent)
  542. (define-key map "f" 'ycmd-fixit)
  543. (define-key map "r" 'ycmd-refactor-rename)
  544. (define-key map "x" 'ycmd-completer)
  545. map)
  546. "Keymap for `ycmd-mode' interactive commands.")
  547. (defcustom ycmd-keymap-prefix (kbd "C-c Y")
  548. "Prefix for key bindings of `ycmd-mode'.
  549. Changing this variable outside Customize does not have any
  550. effect. To change the keymap prefix from Lisp, you need to
  551. explicitly re-define the prefix key:
  552. (define-key ycmd-mode-map ycmd-keymap-prefix nil)
  553. (setq ycmd-keymap-prefix (kbd \"C-c ,\"))
  554. (define-key ycmd-mode-map ycmd-keymap-prefix
  555. ycmd-command-map)"
  556. :type 'string
  557. :risky t
  558. :set
  559. (lambda (variable key)
  560. (when (and (boundp variable) (boundp 'ycmd-mode-map))
  561. (define-key ycmd-mode-map (symbol-value variable) nil)
  562. (define-key ycmd-mode-map key ycmd-command-map))
  563. (set-default variable key)))
  564. (defvar ycmd-mode-map
  565. (let ((map (make-sparse-keymap)))
  566. (define-key map ycmd-keymap-prefix ycmd-command-map)
  567. map)
  568. "Keymap for `ycmd-mode'.")
  569. (easy-menu-define ycmd-mode-menu ycmd-mode-map
  570. "Menu used when `ycmd-mode' is active."
  571. '("YCMd"
  572. ["Start server" ycmd-open]
  573. ["Stop server" ycmd-close]
  574. "---"
  575. ["Parse buffer" ycmd-parse-buffer]
  576. "---"
  577. ["GoTo" ycmd-goto]
  578. ["GoToDefinition" ycmd-goto-definition]
  579. ["GoToDeclaration" ycmd-goto-declaration]
  580. ["GoToInclude" ycmd-goto-include]
  581. ["GoToImplementation" ycmd-goto-implementation]
  582. ["GoToReferences" ycmd-goto-references]
  583. ["GoToType" ycmd-goto-type]
  584. ["GoToImprecise" ycmd-goto-imprecise]
  585. "---"
  586. ["Show documentation" ycmd-show-documentation]
  587. ["Show type" ycmd-get-type]
  588. ["Show parent" ycmd-get-parent]
  589. "---"
  590. ["FixIt" ycmd-fixit]
  591. ["RefactorRename" ycmd-refactor-rename]
  592. "---"
  593. ["Load extra config" ycmd-load-conf-file]
  594. ["Restart semantic server" ycmd-restart-semantic-server]
  595. ["Clear compilation flag cache" ycmd-clear-compilation-flag-cache]
  596. ["Force semantic completion" ycmd-toggle-force-semantic-completion
  597. :style toggle :selected ycmd-force-semantic-completion]
  598. "---"
  599. ["Show debug info" ycmd-show-debug-info]
  600. ["Show version" ycmd-version t]
  601. ["Log enabled" ycmd-toggle-log-enabled
  602. :style toggle :selected ycmd--log-enabled]))
  603. (defmacro ycmd--kill-timer (timer)
  604. "Cancel TIMER."
  605. `(when ,timer
  606. (cancel-timer ,timer)
  607. (setq ,timer nil)))
  608. (defun ycmd-parsing-in-progress-p ()
  609. "Return t if parsing is in progress."
  610. (eq ycmd--last-status-change 'parsing))
  611. (defun ycmd--report-status (status)
  612. "Report ycmd STATUS."
  613. (setq ycmd--last-status-change status)
  614. (force-mode-line-update))
  615. (defun ycmd--mode-line-status-text ()
  616. "Get text for the mode line."
  617. (let ((force-semantic
  618. (when ycmd-force-semantic-completion "/s"))
  619. (text (pcase ycmd--last-status-change
  620. (`parsed "")
  621. (`parsing "*")
  622. (`unparsed "?")
  623. (`stopped "-")
  624. (`starting ">")
  625. (`errored "!"))))
  626. (concat " " ycmd-mode-line-prefix force-semantic text)))
  627. ;;;###autoload
  628. (define-minor-mode ycmd-mode
  629. "Minor mode for interaction with the ycmd completion server.
  630. When called interactively, toggle `ycmd-mode'. With prefix ARG,
  631. enable `ycmd-mode' if ARG is positive, otherwise disable it.
  632. When called from Lisp, enable `ycmd-mode' if ARG is omitted,
  633. nil or positive. If ARG is `toggle', toggle `ycmd-mode'.
  634. Otherwise behave as if called interactively.
  635. \\{ycmd-mode-map}"
  636. :init-value nil
  637. :keymap ycmd-mode-map
  638. :lighter (:eval (ycmd--mode-line-status-text))
  639. :group 'ycmd
  640. :require 'ycmd
  641. :after-hook
  642. (progn (unless (ycmd-running-p) (ycmd-open))
  643. (ycmd--conditional-parse 'mode-enabled 'deferred))
  644. (cond
  645. (ycmd-mode
  646. (dolist (hook ycmd-hooks-alist)
  647. (add-hook (car hook) (cdr hook) nil 'local)))
  648. (t
  649. (dolist (hook ycmd-hooks-alist)
  650. (remove-hook (car hook) (cdr hook) 'local))
  651. (ycmd--teardown))))
  652. ;;;###autoload
  653. (defun ycmd-setup ()
  654. "Setup `ycmd-mode'.
  655. Hook `ycmd-mode' into modes in `ycmd-file-type-map'."
  656. (interactive)
  657. (dolist (it ycmd-file-type-map)
  658. (add-hook (intern (format "%s-hook" (symbol-name (car it)))) 'ycmd-mode)))
  659. (make-obsolete 'ycmd-setup 'global-ycmd-mode "1.0")
  660. (defun ycmd-version (&optional show-version)
  661. "Get the `emacs-ycmd' version as string.
  662. If called interactively or if SHOW-VERSION is non-nil, show the
  663. version in the echo area and the messages buffer.
  664. The returned string includes both, the version from package.el
  665. and the library version, if both a present and different.
  666. If the version number could not be determined, signal an error,
  667. if called interactively, or if SHOW-VERSION is non-nil, otherwise
  668. just return nil."
  669. (interactive (list t))
  670. (if (require 'pkg-info nil :no-error)
  671. (let ((version (pkg-info-version-info 'ycmd)))
  672. (when show-version
  673. (message "emacs-ycmd version: %s" version))
  674. version)
  675. (error "Cannot determine version without package pkg-info")))
  676. (defun ycmd--maybe-enable-mode ()
  677. "Enable `ycmd-mode' according `ycmd-global-modes'."
  678. (when (pcase ycmd-global-modes
  679. (`t (ycmd-major-mode-to-file-types major-mode))
  680. (`all t)
  681. (`(not . ,modes) (not (memq major-mode modes)))
  682. (modes (memq major-mode modes)))
  683. (ycmd-mode)))
  684. ;;;###autoload
  685. (define-globalized-minor-mode global-ycmd-mode ycmd-mode
  686. ycmd--maybe-enable-mode
  687. :init-value nil)
  688. (defun ycmd-unload-function ()
  689. "Unload function for ycmd."
  690. (global-ycmd-mode -1)
  691. (remove-hook 'kill-emacs-hook #'ycmd-close))
  692. (defvar-local ycmd--deferred-parse nil
  693. "If non-nil, a deferred file parse notification is pending.")
  694. (defun ycmd--must-defer-parse ()
  695. "Determine whether parsing the file has to be deferred.
  696. Return t if parsing is to be deferred, or nil otherwise."
  697. (or (not (ycmd--server-alive-p))
  698. (not (get-buffer-window))
  699. (ycmd-parsing-in-progress-p)
  700. revert-buffer-in-progress-p))
  701. (defun ycmd--deferred-parse-p ()
  702. "Return non-nil if current buffer has a deferred parse."
  703. ycmd--deferred-parse)
  704. (defun ycmd--parse-deferred ()
  705. "Defer parse notification for current buffer."
  706. (setq ycmd--deferred-parse t))
  707. (defun ycmd--perform-deferred-parse ()
  708. "Perform the deferred parse."
  709. (when (ycmd--deferred-parse-p)
  710. (setq ycmd--deferred-parse nil)
  711. (ycmd--conditional-parse)))
  712. (defun ycmd--conditional-parse (&optional condition force-deferred)
  713. "Reparse the buffer under CONDITION.
  714. If CONDITION is non-nil, determine whether a ready to parse
  715. notification should be sent according `ycmd-parse-conditions'.
  716. If FORCE-DEFERRED is non-nil perform parse notification later."
  717. (when (and ycmd-mode
  718. (or (not condition)
  719. (memq condition ycmd-parse-conditions)))
  720. (if (or force-deferred (ycmd--must-defer-parse))
  721. (ycmd--parse-deferred)
  722. (let ((buffer (current-buffer)))
  723. (deferred:$
  724. (deferred:next
  725. (lambda ()
  726. (with-current-buffer buffer
  727. (ycmd--on-visit-buffer))))
  728. (deferred:nextc it
  729. (lambda ()
  730. (with-current-buffer buffer
  731. (let ((tick (buffer-chars-modified-tick)))
  732. (unless (equal tick ycmd--last-modified-tick)
  733. (setq ycmd--last-modified-tick tick)
  734. (ycmd-notify-file-ready-to-parse)))))))))))
  735. (defun ycmd--on-save ()
  736. "Function to run when the buffer has been saved."
  737. (ycmd--conditional-parse 'save))
  738. (defun ycmd--on-idle-change ()
  739. "Function to run on idle-change."
  740. (ycmd--kill-timer ycmd--notification-timer)
  741. (ycmd--conditional-parse 'idle-change))
  742. (defun ycmd--on-change (beg end _len)
  743. "Function to run when a buffer change between BEG and END.
  744. _LEN is ununsed."
  745. (save-match-data
  746. (when ycmd-mode
  747. (ycmd--kill-timer ycmd--notification-timer)
  748. (if (string-match-p "\n" (buffer-substring beg end))
  749. (ycmd--conditional-parse 'new-line)
  750. (setq ycmd--notification-timer
  751. (run-at-time ycmd-idle-change-delay nil
  752. #'ycmd--on-idle-change))))))
  753. (defun ycmd--on-unparsed-buffer-focus (buffer)
  754. "Function to run when an unparsed BUFFER gets focus."
  755. (ycmd--kill-timer ycmd--on-focus-timer)
  756. (with-current-buffer buffer
  757. (ycmd--conditional-parse 'buffer-focus)))
  758. (defun ycmd--on-window-configuration-change ()
  759. "Function to run by `window-configuration-change-hook'."
  760. (if (ycmd--deferred-parse-p)
  761. (ycmd--perform-deferred-parse)
  762. (when (and ycmd-mode
  763. (pcase ycmd--last-status-change
  764. ((or `unparsed `starting) t))
  765. (memq 'buffer-focus ycmd-parse-conditions))
  766. (ycmd--kill-timer ycmd--on-focus-timer)
  767. (let ((on-buffer-focus-fn
  768. (apply-partially 'ycmd--on-unparsed-buffer-focus
  769. (current-buffer))))
  770. (setq ycmd--on-focus-timer
  771. (run-at-time 1.0 nil on-buffer-focus-fn))))))
  772. (defmacro ycmd--with-all-ycmd-buffers (&rest body)
  773. "Execute BODY with each `ycmd-mode' enabled buffer."
  774. (declare (indent 0) (debug t))
  775. `(dolist (buffer (buffer-list))
  776. (with-current-buffer buffer
  777. (when ycmd-mode
  778. ,@body))))
  779. (defun ycmd--exception-p (response)
  780. "Check whether RESPONSE is an exception."
  781. (and (listp response) (assq 'exception response)))
  782. (cl-defmacro ycmd-with-handled-server-exceptions (request
  783. &rest body
  784. &key
  785. dont-show-exception-msg
  786. on-exception-form
  787. return-form
  788. bind-current-buffer
  789. &allow-other-keys)
  790. "Run a deferred REQUEST and exectute BODY on success. Catch all
  791. exceptions raised through server communication. If it is raised
  792. because of a unknown .ycm_extra_conf.py file, load the file or
  793. ignore it after asking the user. Otherwise print exception to
  794. minibuffer if NO-EXCEPTION-MESSAGE is nil. ON-EXCEPTION-FORM is
  795. run if an exception occurs. The value of RETURN-FORM is returned
  796. on exception. If BIND-CURRENT-BUFFER is non-nil, bind
  797. `current-buffer' to `request-buffer' var.
  798. \(fn REQUEST &key NO-DISPLAY ERROR-FORM RETURN-FORM
  799. BIND-CURRENT-BUFFER &rest BODY)"
  800. (declare (indent 1) (debug t))
  801. (while (keywordp (car body))
  802. (setq body (cdr (cdr body))))
  803. `(let ((request-buffer (and ,bind-current-buffer (current-buffer))))
  804. (deferred:$
  805. ,request
  806. (deferred:nextc it
  807. (lambda (response)
  808. (cl-macrolet ((with-optional-current-buffer
  809. (buffer-or-name &rest body-2)
  810. `(if ,buffer-or-name
  811. (with-current-buffer ,buffer-or-name
  812. ,@body-2)
  813. ,@body-2)))
  814. (with-optional-current-buffer
  815. request-buffer
  816. (if (ycmd--exception-p response)
  817. (let-alist response
  818. (if (string= .exception.TYPE "UnknownExtraConf")
  819. (ycmd--handle-extra-conf-exception
  820. .exception.extra_conf_file)
  821. (unless ,dont-show-exception-msg
  822. (message "%s: %s" .exception.TYPE .message))
  823. ,on-exception-form
  824. ,return-form))
  825. (if (null ',body) response ,@body)))))))))
  826. (defun ycmd--on-visit-buffer ()
  827. "If `ycmd--buffer-visit-flag' is nil send BufferVisit event."
  828. (when (and (not ycmd--buffer-visit-flag)
  829. (ycmd--server-alive-p))
  830. (ycmd-with-handled-server-exceptions
  831. (ycmd--event-notification "BufferVisit")
  832. :bind-current-buffer t
  833. (setq ycmd--buffer-visit-flag t))))
  834. (defun ycmd--on-close-buffer ()
  835. "Notify server that the current buffer is no longer open.
  836. Cleanup emacs-ycmd variables."
  837. (when (ycmd--server-alive-p)
  838. (ycmd-with-handled-server-exceptions
  839. (ycmd--event-notification "BufferUnload")))
  840. (ycmd--teardown))
  841. (defun ycmd--reset-parse-status ()
  842. (ycmd--report-status 'unparsed)
  843. (setq ycmd--last-modified-tick nil))
  844. (defun ycmd--teardown ()
  845. "Teardown ycmd in current buffer."
  846. (ycmd--kill-timer ycmd--notification-timer)
  847. (ycmd--reset-parse-status)
  848. (setq ycmd--deferred-parse nil)
  849. (run-hooks 'ycmd-after-teardown-hook))
  850. (defun ycmd--global-teardown ()
  851. "Teardown ycmd in all buffers."
  852. (ycmd--kill-timer ycmd--on-focus-timer)
  853. (setq ycmd--mode-keywords-loaded nil)
  854. (clrhash ycmd--available-completers)
  855. (ycmd--with-all-ycmd-buffers
  856. (ycmd--teardown)
  857. (setq ycmd--buffer-visit-flag nil)))
  858. (defun ycmd-file-types-with-diagnostics (mode)
  859. "Find the ycmd file types for MODE which support semantic diagnostics.
  860. Returns a possibly empty list of ycmd file type strings. If this
  861. is empty, then ycmd doesn't support semantic completion (or
  862. diagnostics) for MODE."
  863. (-intersection
  864. ycmd--file-types-with-diagnostics
  865. (ycmd-major-mode-to-file-types mode)))
  866. (defun ycmd-major-modes-with-diagnostics ()
  867. "Return a list with major-modes which support semantic diagnostics."
  868. (->>
  869. (--filter (member (cadr it) ycmd--file-types-with-diagnostics)
  870. ycmd-file-type-map)
  871. (--map (car it))))
  872. (defun ycmd-deferred:sync! (d)
  873. "Wait for the given deferred task.
  874. Error is raised if it is not processed within deferred chain D.
  875. This is a slightly modified version of the original
  876. `deferred:sync!' function, with using `accept-process-output'
  877. wrapped with `with-current-buffer' for waiting instead of a
  878. combination of `sit-for' and `sleep-for' and with a shorter wait
  879. time."
  880. (progn
  881. (let ((last-value 'deferred:undefined*)
  882. uncaught-error)
  883. (deferred:try
  884. (deferred:nextc d
  885. (lambda (x) (setq last-value x)))
  886. :catch
  887. (lambda (err) (setq uncaught-error err)))
  888. (with-local-quit
  889. (while (and (eq 'deferred:undefined* last-value)
  890. (not uncaught-error))
  891. (accept-process-output nil 0.01))
  892. (when uncaught-error
  893. (deferred:resignal uncaught-error))
  894. last-value))))
  895. (defmacro ycmd-deferred:timeout (timeout-sec d)
  896. "Time out macro on a deferred task.
  897. If the deferred task does not complete within TIMEOUT-SEC, this
  898. macro cancels the deferred task D and returns nil. This is a
  899. slightly modified version of the original `deferred:timeout'
  900. macro, which takes the timeout var in seconds and the timeout
  901. form returns the symbol `timeout'."
  902. (declare (indent 1) (debug t))
  903. `(deferred:earlier
  904. (deferred:nextc (deferred:wait (* ,timeout-sec 1000))
  905. (lambda () 'timeout))
  906. ,d))
  907. (defun ycmd-open ()
  908. "Start a new ycmd server.
  909. This kills any ycmd server already running (under ycmd.el's
  910. control.)."
  911. (interactive)
  912. (ycmd-close)
  913. (ycmd--start-server)
  914. (ycmd--start-server-timeout-timer)
  915. (ycmd--start-keepalive-timer))
  916. (defun ycmd-close (&optional status)
  917. "Shutdown any running ycmd server.
  918. STATUS is a status symbol for `ycmd--report-status',
  919. defaulting to `stopped'."
  920. (interactive)
  921. (ycmd--stop-server)
  922. (ycmd--global-teardown)
  923. (ycmd--kill-timer ycmd--keepalive-timer)
  924. (ycmd--kill-timer ycmd--server-timeout-timer)
  925. (ycmd--with-all-ycmd-buffers
  926. (ycmd--report-status (or status 'stopped))))
  927. (defun ycmd--stop-server ()
  928. "Stop the ycmd server process.
  929. Send a `shutdown' request to the ycmd server and wait for the
  930. ycmd server to stop. If the ycmd server is still running after a
  931. timeout specified by `ycmd-delete-process-delay', then kill the
  932. process with `delete-process'."
  933. (when (ycmd--server-alive-p)
  934. (let ((start-time (float-time)))
  935. (ycmd-deferred:sync!
  936. (ycmd-deferred:timeout 0.1
  937. (ycmd-with-handled-server-exceptions
  938. (ycmd--request (make-ycmd-request-data
  939. :handler "shutdown" :content nil))
  940. :dont-show-exception-msg t)))
  941. (while (and (ycmd-running-p)
  942. (> ycmd-delete-process-delay
  943. (- (float-time) start-time)))
  944. (sit-for 0.05))))
  945. (ignore-errors
  946. (delete-process ycmd--server-process-name)))
  947. (defun ycmd-running-p ()
  948. "Return t if a ycmd server is already running."
  949. (--when-let (get-process ycmd--server-process-name)
  950. (and (processp it) (process-live-p it) t)))
  951. (defun ycmd--server-alive-p ()
  952. "Return t if server is running and ready for requests."
  953. (and (ycmd-running-p) ycmd--server-actual-port))
  954. (defmacro ycmd--ignore-errors (&rest body)
  955. "Execute BODY and ignore errors and request errors."
  956. `(let ((request-message-level -1)
  957. (request-log-level -1))
  958. (ignore-errors
  959. ,@body)))
  960. (defun ycmd--keepalive ()
  961. "Sends an unspecified message to the server.
  962. This is simply for keepalive functionality."
  963. (ycmd--ignore-errors
  964. (ycmd-with-handled-server-exceptions
  965. (ycmd--request (make-ycmd-request-data
  966. :handler "healthy" :content nil)
  967. :type "GET")
  968. :dont-show-exception-msg t)))
  969. (defun ycmd--server-ready-p (&optional include-subserver)
  970. "Send request for server ready state.
  971. If INCLUDE-SUBSERVER is non-nil, also request ready state for
  972. semantic subserver."
  973. (when (ycmd--server-alive-p)
  974. (let* ((file-type
  975. (and include-subserver
  976. (car-safe (ycmd-major-mode-to-file-types
  977. major-mode))))
  978. (params (and file-type
  979. (list (cons "subserver" file-type)))))
  980. (ycmd--ignore-errors
  981. (eq (ycmd-deferred:sync!
  982. (ycmd-with-handled-server-exceptions
  983. (ycmd--request
  984. (make-ycmd-request-data :handler "ready" :content nil)
  985. :params params :type "GET")
  986. :dont-show-exception-msg t))
  987. t)))))
  988. (defun ycmd--extra-conf-request (filename &optional ignore-p)
  989. "Send extra conf request.
  990. FILENAME is the path to a ycm_extra_conf file. If optional
  991. IGNORE-P is non-nil ignore the ycm_extra_conf."
  992. (let ((handler (if ignore-p
  993. "ignore_extra_conf_file"
  994. "load_extra_conf_file"))
  995. (content (list (cons "filepath" filename))))
  996. (ycmd-deferred:sync!
  997. (ycmd-with-handled-server-exceptions
  998. (ycmd--request (make-ycmd-request-data
  999. :handler handler :content content))))))
  1000. (defun ycmd-load-conf-file (filename)
  1001. "Tell the ycmd server to load the configuration file FILENAME."
  1002. (interactive
  1003. (list
  1004. (read-file-name "Filename: ")))
  1005. (let ((filename (expand-file-name filename)))
  1006. (ycmd--extra-conf-request filename)))
  1007. (defun ycmd-display-completions ()
  1008. "Get completions at the current point and display them in a buffer.
  1009. This is really a utility/debugging function for developers, but
  1010. it might be interesting for some users."
  1011. (interactive)
  1012. (ycmd-with-handled-server-exceptions (ycmd-get-completions)
  1013. (if (not response)
  1014. (message "No completions available")
  1015. (pop-to-buffer "*ycmd-completions*")
  1016. (erase-buffer)
  1017. (insert (pp-to-string response)))))
  1018. (defun ycmd-complete (&optional _ignored)
  1019. "Completion candidates at point."
  1020. (-when-let* ((completions (ycmd-deferred:sync!
  1021. (ycmd-deferred:timeout 0.5
  1022. (ycmd-with-handled-server-exceptions
  1023. (ycmd-get-completions)))))
  1024. (candidates (cdr (assq 'completions completions))))
  1025. (--map (let* ((text (cdr (assq 'insertion_text it)))
  1026. (anno (cdr (assq 'menu_text it))))
  1027. (when (and anno (string-match (regexp-quote text) anno))
  1028. (put-text-property
  1029. 0 1 'anno (substring anno (match-end 0)) text))
  1030. text)
  1031. candidates)))
  1032. (defun ycmd-complete-at-point ()
  1033. "Complete symbol at point."
  1034. (unless (nth 3 (syntax-ppss)) ;; not in string
  1035. (let* ((bounds (bounds-of-thing-at-point 'symbol))
  1036. (beg (or (car bounds) (point)))
  1037. (end (or (cdr bounds) (point))))
  1038. (list beg end
  1039. (completion-table-dynamic #'ycmd-complete)
  1040. :annotation-function
  1041. (lambda (arg) (get-text-property 0 'anno arg))))))
  1042. (defun ycmd-toggle-force-semantic-completion ()
  1043. "Toggle whether to use always semantic completion.
  1044. Returns the new value of `ycmd-force-semantic-completion'."
  1045. (interactive)
  1046. (let ((force (not ycmd-force-semantic-completion)))
  1047. (message "ycmd: force semantic completion %s."
  1048. (if force "enabled" "disabled"))
  1049. (setq ycmd-force-semantic-completion force)))
  1050. (defun ycmd--string-list-p (obj)
  1051. "Return t if OBJ is a list of strings."
  1052. (and (listp obj) (-all? #'stringp obj)))
  1053. (defun ycmd--locate-default-tags-file (buffer)
  1054. "Look up directory hierarchy for first found default tags file for BUFFER."
  1055. (-when-let* ((file (buffer-file-name buffer))
  1056. (dir (and file
  1057. (locate-dominating-file
  1058. file ycmd-default-tags-file-name))))
  1059. (expand-file-name ycmd-default-tags-file-name dir)))
  1060. (defun ycmd--get-tag-files (buffer)
  1061. "Get tag files list for current BUFFER or nil."
  1062. (--when-let (cond ((eq ycmd-tag-files 'auto)
  1063. (ycmd--locate-default-tags-file buffer))
  1064. ((or (stringp ycmd-tag-files)
  1065. (ycmd--string-list-p ycmd-tag-files))
  1066. ycmd-tag-files))
  1067. (unless (listp it)
  1068. (setq it (list it)))
  1069. (mapcar 'expand-file-name it)))
  1070. (defun ycmd--get-keywords (buffer)
  1071. "Get syntax keywords for BUFFER."
  1072. (with-current-buffer buffer
  1073. (let ((mode major-mode))
  1074. (unless (memq mode ycmd--mode-keywords-loaded)
  1075. (--when-let (and (functionp ycmd-get-keywords-function)
  1076. (funcall ycmd-get-keywords-function mode))
  1077. (when (ycmd--string-list-p it)
  1078. (add-to-list 'ycmd--mode-keywords-loaded mode)
  1079. it))))))
  1080. (defun ycmd--get-keywords-from-alist (mode)
  1081. "Get keywords from `ycmd-keywords-alist' for MODE."
  1082. (let ((symbols (cdr (assq mode ycmd-keywords-alist))))
  1083. (if (consp symbols)
  1084. symbols
  1085. (cdr (assq symbols ycmd-keywords-alist)))))
  1086. (defun ycmd-get-completions ()
  1087. "Get completions in current buffer from the ycmd server.
  1088. Returns a deferred object which yields the HTTP message
  1089. content. If completions are available, the structure looks like
  1090. this:
  1091. ((error)
  1092. (completion_start_column . 6)
  1093. (completions
  1094. ((kind . \"FUNCTION\")
  1095. (extra_menu_info . \"long double\")
  1096. (detailed_info . \"long double acoshl( long double )\\n\")
  1097. (insertion_text . \"acoshl\")
  1098. (menu_text . \"acoshl( long double )\"))
  1099. . . .))
  1100. If ycmd can't do completion because it's busy parsing, the
  1101. structure looks like this:
  1102. ((message . \"Still parsing file, no completions yet.\")
  1103. (traceback . \"long traceback string\")
  1104. (exception
  1105. (TYPE . \"RuntimeError\")))
  1106. To see what the returned structure looks like, you can use
  1107. `ycmd-display-completions'."
  1108. (let ((extra-data (and ycmd-force-semantic-completion
  1109. (list (cons "force_semantic" t)))))
  1110. (ycmd--request (make-ycmd-request-data
  1111. :handler "completions"
  1112. :content (append (ycmd--get-basic-request-data)
  1113. extra-data)))))
  1114. (defun ycmd--command-request (subcommand)
  1115. "Send a command request for SUBCOMMAND."
  1116. (let* ((subcommand (if (listp subcommand)
  1117. subcommand
  1118. (list subcommand)))
  1119. (content (cons (append (list "command_arguments")
  1120. subcommand)
  1121. (ycmd--get-basic-request-data))))
  1122. (ycmd--request (make-ycmd-request-data
  1123. :handler "run_completer_command"
  1124. :content content))))
  1125. (defun ycmd--run-completer-command (subcommand success-handler)
  1126. "Send SUBCOMMAND to the `ycmd' server.
  1127. SUCCESS-HANDLER is called when for a successful response."
  1128. (declare (indent 1))
  1129. (when ycmd-mode
  1130. (let ((cmd (or (car-safe subcommand) subcommand)))
  1131. (if (ycmd-parsing-in-progress-p)
  1132. (message "Can't send \"%s\" request while parsing is in progress!" cmd)
  1133. (let ((pos (point)))
  1134. (ycmd-with-handled-server-exceptions (ycmd--command-request subcommand)
  1135. :bind-current-buffer t
  1136. :on-exception-form (run-hook-with-args 'ycmd-after-exception-hook
  1137. cmd request-buffer pos response)
  1138. (when (and response success-handler)
  1139. (funcall success-handler response))))))))
  1140. (defun ycmd--unsupported-subcommand-p (response)
  1141. "Return t if RESPONSE is an unsupported subcommand exception."
  1142. (let-alist response
  1143. (and (string= "ValueError" .exception.TYPE)
  1144. (or (string-prefix-p "Supported commands are:\n" .message)
  1145. (string= "This Completer has no supported subcommands."
  1146. .message)))))
  1147. (defun ycmd--get-defined-subcommands ()
  1148. "Get available subcommands for current completer.
  1149. This is a blocking request."
  1150. (let* ((data (make-ycmd-request-data
  1151. :handler "defined_subcommands")))
  1152. (ycmd-deferred:sync!
  1153. (ycmd-with-handled-server-exceptions (ycmd--request data)))))
  1154. (defun ycmd--get-prompt-for-subcommand (subcommand)
  1155. "Return promp for SUBCOMMAND that requires arguments."
  1156. (pcase subcommand
  1157. (`"RefactorRename"
  1158. "New name: ")
  1159. ((pred (and "RestartServer" (eq major-mode 'python-mode)))
  1160. "Python binary: ")))
  1161. (defun ycmd--read-subcommand ()
  1162. "Read subcommand from minibuffer."
  1163. (--when-let (ycmd--get-defined-subcommands)
  1164. (funcall ycmd-completing-read-function
  1165. "Subcommand: " it nil t)))
  1166. (defun ycmd-completer (subcommand)
  1167. "Run SUBCOMMAND for current completer."
  1168. (interactive
  1169. (list (-when-let (cmd (ycmd--read-subcommand))
  1170. (--when-let (ycmd--get-prompt-for-subcommand cmd)
  1171. (let ((arg (read-string it)))
  1172. (unless (s-blank-str? arg)
  1173. (setq cmd (list cmd arg)))))
  1174. cmd)))
  1175. (when subcommand
  1176. (ycmd--run-completer-command subcommand
  1177. (lambda (response)
  1178. (cond ((not (listp response))
  1179. ;; If not a list, the response is necessarily a scalar:
  1180. ;; boolean, number, string, etc. In this case, we print it to
  1181. ;; the user.
  1182. (message "%s" response))
  1183. ((assq 'fixits response)
  1184. (ycmd--handle-fixit-response response))
  1185. ((assq 'message response)
  1186. (ycmd--handle-message-response response))
  1187. ((assq 'detailed_info response)
  1188. (ycmd--handle-detailed-info-response response))
  1189. (t
  1190. (ycmd--handle-goto-response response)))))))
  1191. (defun ycmd-goto ()
  1192. "Go to the definition or declaration of the symbol at current position."
  1193. (interactive)
  1194. (ycmd--goto "GoTo"))
  1195. (defun ycmd-goto-declaration ()
  1196. "Go to the declaration of the symbol at the current position."
  1197. (interactive)
  1198. (ycmd--goto "GoToDeclaration"))
  1199. (defun ycmd-goto-definition ()
  1200. "Go to the definition of the symbol at the current position."
  1201. (interactive)
  1202. (ycmd--goto "GoToDefinition"))
  1203. (defun ycmd-goto-implementation ()
  1204. "Go to the implementation of the symbol at the current position."
  1205. (interactive)
  1206. (ycmd--goto "GoToImplementation"))
  1207. (defun ycmd-goto-include ()
  1208. "Go to the include of the symbol at the current position."
  1209. (interactive)
  1210. (ycmd--goto "GoToInclude"))
  1211. (defun ycmd-goto-imprecise ()
  1212. "Fast implementation of Go To at the cost of precision.
  1213. Useful in case compile-time is considerable."
  1214. (interactive)
  1215. (ycmd--goto "GoToImprecise"))
  1216. (defun ycmd-goto-references ()
  1217. "Get references."
  1218. (interactive)
  1219. (ycmd--goto "GoToReferences"))
  1220. (defun ycmd-goto-type ()
  1221. "Go to the type of the symbol at the current position."
  1222. (interactive)
  1223. (ycmd--goto "GoToType"))
  1224. (defun ycmd--save-marker ()
  1225. "Save marker."
  1226. (push-mark)
  1227. (if (fboundp 'xref-push-marker-stack)
  1228. (xref-push-marker-stack)
  1229. (with-no-warnings
  1230. (ring-insert find-tag-marker-ring (point-marker)))))
  1231. (defun ycmd--location-data-p (response)
  1232. "Return t if RESPONSE is a GoTo location."
  1233. (and (assq 'filepath response)
  1234. (assq 'line_num response)
  1235. (assq 'column_num response)))
  1236. (defun ycmd--handle-goto-response (response)
  1237. "Handle a successfull GoTo RESPONSE."
  1238. (ycmd--save-marker)
  1239. (if (ycmd--location-data-p response)
  1240. (ycmd--goto-location response 'find-file)
  1241. (ycmd--view response major-mode)))
  1242. (defun ycmd--goto (type)
  1243. "Implementation of GoTo according to the request TYPE."
  1244. (save-excursion
  1245. (--when-let (bounds-of-thing-at-point 'symbol)
  1246. (goto-char (car it)))
  1247. (ycmd-completer type)))
  1248. (defun ycmd--goto-location (location find-function)
  1249. "Move cursor to LOCATION with FIND-FUNCTION.
  1250. LOCATION is a structure as returned from e.g. the various GoTo
  1251. commands."
  1252. (let-alist location
  1253. (when .filepath
  1254. (funcall find-function .filepath)
  1255. (goto-char (ycmd--col-line-to-position
  1256. .column_num .line_num)))))
  1257. (defun ycmd--goto-line (line)
  1258. "Go to LINE."
  1259. (goto-char (point-min))
  1260. (forward-line (1- line)))
  1261. (defun ycmd--col-line-to-position (col line &optional buffer)
  1262. "Convert COL and LINE into a position in the current buffer.
  1263. COL and LINE are expected to be as returned from ycmd, e.g. from
  1264. notify-file-ready. Apparently COL can be 0 sometimes, in which
  1265. case this function returns 0.
  1266. Use BUFFER if non-nil or `current-buffer'."
  1267. (let ((buff (or buffer (current-buffer))))
  1268. (if (= col 0)
  1269. 0
  1270. (with-current-buffer buff
  1271. (ycmd--goto-line line)
  1272. (forward-char (- col 1))
  1273. (point)))))
  1274. (defun ycmd-clear-compilation-flag-cache ()
  1275. "Clear the compilation flags cache."
  1276. (interactive)
  1277. (ycmd-completer "ClearCompilationFlagCache"))
  1278. (defun ycmd-restart-semantic-server (&optional arg)
  1279. "Send request to restart the semantic completion backend server.
  1280. If ARG is non-nil and current `major-mode' is `python-mode',
  1281. prompt for the Python binary."
  1282. (interactive
  1283. (list (and current-prefix-arg
  1284. (eq major-mode 'python-mode)
  1285. (read-string "Python binary: "))))
  1286. (let ((subcommand "RestartServer"))
  1287. (unless (s-blank-str? arg)
  1288. (setq subcommand (list subcommand arg)))
  1289. (ycmd-completer subcommand)))
  1290. (cl-defun ycmd--fontify-code (code &optional (mode major-mode))
  1291. "Fontify CODE."
  1292. (cl-check-type mode function)
  1293. (if (not (stringp code))
  1294. code
  1295. (with-temp-buffer
  1296. (delay-mode-hooks (funcall mode))
  1297. (setq font-lock-mode t)
  1298. (funcall font-lock-function font-lock-mode)
  1299. (let ((inhibit-read-only t))
  1300. (erase-buffer)
  1301. (insert code)
  1302. (font-lock-default-fontify-region
  1303. (point-min) (point-max) nil))
  1304. (buffer-string))))
  1305. (defun ycmd--get-message (response)
  1306. "Extract message from RESPONSE.
  1307. Return a cons cell with the type or parent as car. If cdr is
  1308. non-nil, the result is a valid type or parent."
  1309. (--when-let (cdr (assq 'message response))
  1310. (pcase it
  1311. ((or `"Unknown semantic parent"
  1312. `"Unknown type"
  1313. `"Internal error: cursor not valid"
  1314. `"Internal error: no translation unit")
  1315. (cons it nil))
  1316. (_ (cons it t)))))
  1317. (defun ycmd--handle-message-response (response)
  1318. "Handle a successful GetParent or GetType RESPONSE."
  1319. (--when-let (ycmd--get-message response)
  1320. (pcase-let ((`(,msg . ,is-type-p) it))
  1321. (message "%s" (if is-type-p
  1322. (ycmd--fontify-code msg)
  1323. msg)))))
  1324. (defun ycmd-get-parent ()
  1325. "Get semantic parent for symbol at point."
  1326. (interactive)
  1327. (ycmd-completer "GetParent"))
  1328. (defun ycmd-get-type (&optional arg)
  1329. "Get type for symbol at point.
  1330. If optional ARG is non-nil, get type without reparsing buffer."
  1331. (interactive "P")
  1332. (ycmd-completer (if arg "GetTypeImprecise" "GetType")))
  1333. ;;; FixIts
  1334. (defmacro ycmd--loop-chunks-by-filename (spec &rest body)
  1335. "Loop over an alist of fixit chunks grouped by filepath.
  1336. Evaluate BODY with `it' bound to each car from FIXIT-CHUNKS, in
  1337. turn. The structure of `it' is a cons cell (FILEPATH CHUNK-LIST).
  1338. Then evaluate RESULT to get return value, default nil.
  1339. \(fn (FIXIT-CHUNKS [RESULT]) BODY...)"
  1340. (declare (indent 1) (debug ((form &optional form) body)))
  1341. `(let ((chunks-by-filepath
  1342. (--group-by (let-alist it .range.start.filepath)
  1343. ,(car spec))))
  1344. (dolist (it chunks-by-filepath)
  1345. ,@body)
  1346. ,@(cdr spec)))
  1347. (defun ycmd--show-fixits (fixits &optional title)
  1348. "Select a buffer and display FIXITS.
  1349. Optional TITLE is shown on first line."
  1350. (let ((fixits-buffer (get-buffer-create "*ycmd-fixits*"))
  1351. (fixit-num 1))
  1352. (with-current-buffer fixits-buffer
  1353. (setq buffer-read-only nil)
  1354. (erase-buffer)
  1355. (when title (insert (propertize title 'face 'bold)))
  1356. (dolist (fixit fixits)
  1357. (let-alist fixit
  1358. (let* ((diffs (ycmd--get-fixit-diffs .chunks))
  1359. (multiple-fixits-p (> (length diffs) 1))
  1360. button-diff)
  1361. (dolist (diff diffs)
  1362. (pcase-let ((`(,diff-text . ,diff-path) diff))
  1363. (setq button-diff
  1364. (concat button-diff (when (or (s-blank-str? .text)
  1365. multiple-fixits-p)
  1366. (format "%s\n" diff-path))
  1367. (ycmd--fontify-code diff-text 'diff-mode) "\n"))))
  1368. (ycmd--insert-fixit-button
  1369. (concat (format "%d: %s\n" fixit-num .text) button-diff) .chunks)
  1370. (cl-incf fixit-num))))
  1371. (goto-char (point-min))
  1372. (when title (forward-line 1))
  1373. (ycmd-fixit-mode))
  1374. (pop-to-buffer fixits-buffer)
  1375. (setq next-error-last-buffer fixits-buffer)))
  1376. (defun ycmd--get-fixit-diffs (chunks)
  1377. "Return a list of diffs for CHUNKS.
  1378. Each diff is a list of the actual diff, the path of the file for
  1379. the diff and a flag whether to show the filepath as part of the
  1380. button text. The flag is set to t when there are multiple diff
  1381. chunks for the file."
  1382. (let (diffs)
  1383. (ycmd--loop-chunks-by-filename (chunks (nreverse diffs))
  1384. (pcase-let* ((`(,filepath . ,chunk) it)
  1385. (buffer (find-file-noselect filepath))
  1386. (buffertext (with-current-buffer buffer (buffer-string)))
  1387. (diff-buffer (with-temp-buffer
  1388. (insert buffertext)
  1389. (ycmd--replace-chunk-list chunk (current-buffer))
  1390. (diff-no-select buffer (current-buffer)
  1391. "-U0 --strip-trailing-cr" t))))
  1392. (with-current-buffer diff-buffer
  1393. (goto-char (point-min))
  1394. (unless (eobp)
  1395. (ignore-errors
  1396. (diff-beginning-of-hunk t))
  1397. (while (looking-at diff-hunk-header-re-unified)
  1398. (let* ((beg (point))
  1399. (end (diff-end-of-hunk))
  1400. (diff (buffer-substring-no-properties beg end)))
  1401. (push (cons diff filepath) diffs)))))))))
  1402. (define-button-type 'ycmd--fixit-button
  1403. 'action #'ycmd--apply-fixit
  1404. 'face nil)
  1405. (defun ycmd--insert-fixit-button (name fixit)
  1406. "Insert a button with NAME and FIXIT."
  1407. (insert-text-button
  1408. name
  1409. 'type 'ycmd--fixit-button
  1410. 'fixit fixit))
  1411. (defun ycmd--apply-fixit (button)
  1412. "Apply BUTTON's FixIt chunk."
  1413. (-when-let (chunks (button-get button 'fixit))
  1414. (ycmd--loop-chunks-by-filename (chunks)
  1415. (ycmd--replace-chunk-list (cdr it)))
  1416. (quit-window t (get-buffer-window "*ycmd-fixits*"))))
  1417. (define-derived-mode ycmd-fixit-mode ycmd-view-mode "ycmd-fixits"
  1418. "Major mode for viewing and navigation of fixits.
  1419. \\{ycmd-view-mode-map}"
  1420. (local-set-key (kbd "q") (lambda () (interactive) (quit-window t))))
  1421. (defun ycmd--replace-chunk (bounds replacement-text line-delta char-delta buffer)
  1422. "Replace text between BOUNDS with REPLACEMENT-TEXT.
  1423. BOUNDS is a list of two cons cells representing the start and end
  1424. of a chunk with a line and column pair (car and cdr). LINE-DELTA
  1425. and CHAR-DELTA are offset from -former replacements on the
  1426. current line. BUFFER is the current working buffer."
  1427. (pcase-let* ((`((,start-line . ,start-column) (,end-line . ,end-column)) bounds)
  1428. (start-line (+ start-line line-delta))
  1429. (end-line (+ end-line line-delta))
  1430. (source-line-count (1+ (- end-line start-line)))
  1431. (start-column (+ start-column char-delta))
  1432. (end-column (if (= source-line-count 1)
  1433. (+ end-column char-delta)
  1434. end-column))
  1435. (replacement-lines (s-split "\n" replacement-text))
  1436. (replacement-lines-count (length replacement-lines))
  1437. (new-line-delta (- replacement-lines-count source-line-count))
  1438. (new-char-delta (- (length (car (last replacement-lines)))
  1439. (- end-column start-column))))
  1440. (when (> replacement-lines-count 1)
  1441. (setq new-char-delta (- new-char-delta start-column)))
  1442. (save-excursion
  1443. (with-current-buffer buffer
  1444. (delete-region
  1445. (ycmd--col-line-to-position start-column start-line buffer)
  1446. (ycmd--col-line-to-position end-column end-line buffer))
  1447. (insert replacement-text)
  1448. (cons new-line-delta new-char-delta)))))
  1449. (defun ycmd--get-chunk-bounds (chunk)
  1450. "Get an list with bounds of CHUNK."
  1451. (let-alist chunk
  1452. (list (cons .range.start.line_num .range.start.column_num)
  1453. (cons .range.end.line_num .range.end.column_num))))
  1454. (defun ycmd--chunk-< (c1 c2)
  1455. "Return t if C1 should go before C2."
  1456. (pcase-let ((`((,line-num-1 . ,column-num-1) ,_) (ycmd--get-chunk-bounds c1))
  1457. (`((,line-num-2 . ,column-num-2) ,_) (ycmd--get-chunk-bounds c2)))
  1458. (or (< line-num-1 line-num-2)
  1459. (and (= line-num-1 line-num-2)
  1460. (< column-num-1 column-num-2)))))
  1461. (defun ycmd--replace-chunk-list (chunks &optional buffer)
  1462. "Replace list of CHUNKS.
  1463. If BUFFER is specified use it as working buffer, else use buffer
  1464. specified in fixit chunk."
  1465. (let ((chunks-sorted (sort chunks 'ycmd--chunk-<))
  1466. (last-line -1)
  1467. (line-delta 0)
  1468. (char-delta 0))
  1469. (dolist (c chunks-sorted)
  1470. (let-alist c
  1471. (pcase-let* ((chunk-bounds (ycmd--get-chunk-bounds c))
  1472. (`((,start-line . ,_) (,end-line . ,_)) chunk-bounds)
  1473. (buffer (or buffer (find-file-noselect
  1474. .range.start.filepath))))
  1475. (unless (= start-line last-line)
  1476. (setq last-line end-line)
  1477. (setq char-delta 0))
  1478. (pcase-let ((`(,new-line-delta . ,new-char-delta)
  1479. (ycmd--replace-chunk chunk-bounds .replacement_text
  1480. line-delta char-delta buffer)))
  1481. (setq line-delta (+ line-delta new-line-delta))
  1482. (setq char-delta (+ char-delta new-char-delta))))))))
  1483. (defun ycmd--fixits-have-same-location-p (fixits)
  1484. "Check if mutiple FIXITS have the same location."
  1485. (let ((fixits-by-location
  1486. (--group-by (cdr (assq 'location it)) fixits)))
  1487. (catch 'done
  1488. (dolist (f fixits-by-location)
  1489. (when (> (length (cdr f)) 1)
  1490. (throw 'done t))))))
  1491. (defun ycmd--handle-fixit-response (response)
  1492. "Handle a fixit RESPONSE."
  1493. (let ((fixits (cdr (assq 'fixits response))))
  1494. (if (not fixits)
  1495. (message "No fixits found for current line")
  1496. (let ((multiple-fixits-p
  1497. (and (> (length fixits) 1)
  1498. (ycmd--fixits-have-same-location-p fixits))))
  1499. (if (and (not ycmd-confirm-fixit) (not multiple-fixits-p))
  1500. (let ((num-changes-applied 0)
  1501. files-changed)
  1502. (dolist (fixit fixits)
  1503. (-when-let (chunks (cdr (assq 'chunks fixit)))
  1504. (ycmd--loop-chunks-by-filename (chunks)
  1505. (pcase-let ((`(,chunk-path . ,chunk) it))
  1506. (ycmd--replace-chunk-list chunk)
  1507. (cl-incf num-changes-applied (length chunk))
  1508. (unless (member chunk-path files-changed)
  1509. (setq files-changed (append (list chunk-path)
  1510. files-changed)))))))
  1511. (when (> num-changes-applied 0)
  1512. (let* ((num-files-changed (length files-changed))
  1513. (text
  1514. (concat (format "Applied %d changes" num-changes-applied)
  1515. (when (> num-files-changed 1)
  1516. (format " in %d files" num-files-changed)))))
  1517. (message text))))
  1518. (save-current-buffer
  1519. (ycmd--show-fixits
  1520. fixits (and multiple-fixits-p
  1521. (concat
  1522. "Multiple FixIt suggestions are available at this location."
  1523. "Which one would you like to apply?\n")))))))))
  1524. (defun ycmd-fixit()
  1525. "Get FixIts for current line."
  1526. (interactive)
  1527. (ycmd-completer "FixIt"))
  1528. (defun ycmd-refactor-rename (new-name)
  1529. "Refactor current context with NEW-NAME."
  1530. (interactive "MNew variable name: ")
  1531. (let ((subcommand "RefactorRename"))
  1532. (unless (s-blank-str? new-name)
  1533. (setq subcommand (list subcommand new-name)))
  1534. (ycmd-completer subcommand)))
  1535. (defun ycmd-show-documentation (&optional arg)
  1536. "Show documentation for current point in buffer.
  1537. If optional ARG is non-nil do not reparse buffer before getting
  1538. the documentation."
  1539. (interactive "P")
  1540. (ycmd-completer (if arg "GetDocImprecise" "GetDoc")))
  1541. (defun ycmd--handle-detailed-info-response (response)
  1542. "Handle successful GetDoc RESPONSE."
  1543. (let ((documentation (cdr (assq 'detailed_info response))))
  1544. (if (not (s-blank? documentation))
  1545. (with-help-window (get-buffer-create " *ycmd-documentation*")
  1546. (with-current-buffer standard-output
  1547. (insert documentation)))
  1548. (message "No documentation available for current context"))))
  1549. (defmacro ycmd--with-view-buffer (&rest body)
  1550. "Create view buffer and execute BODY in it."
  1551. `(let ((buf (get-buffer-create "*ycmd-locations*")))
  1552. (with-current-buffer buf
  1553. (setq buffer-read-only nil)
  1554. (erase-buffer)
  1555. ,@body
  1556. (goto-char (point-min))
  1557. (ycmd-view-mode)
  1558. buf)))
  1559. (defun ycmd--view (response mode)
  1560. "Select `ycmd-view-mode' buffer and display items from RESPONSE.
  1561. MODE is a major mode for fontifaction."
  1562. (let ((view-buffer
  1563. (ycmd--with-view-buffer
  1564. (->>
  1565. (--group-by (cdr (assq 'filepath it)) response)
  1566. (mapc (lambda (it) (ycmd--view-insert-location it mode)))))))
  1567. (pop-to-buffer view-buffer)
  1568. (setq next-error-last-buffer view-buffer)))
  1569. (define-button-type 'ycmd--location-button
  1570. 'action #'ycmd--view-jump
  1571. 'face nil)
  1572. (defun ycmd--view-jump (button)
  1573. "Jump to BUTTON's location in current window."
  1574. (let ((location (button-get button 'location)))
  1575. (ycmd--goto-location location 'find-file)))
  1576. (defun ycmd--view-jump-other-window (button)
  1577. "Jump to BUTTON's location in other window."
  1578. (let ((location (button-get button 'location)))
  1579. (ycmd--goto-location location 'find-file-other-window)))
  1580. (defun ycmd--view-insert-button (name location)
  1581. "Insert a view button with NAME and LOCATION."
  1582. (insert-text-button
  1583. name
  1584. 'type 'ycmd--location-button
  1585. 'location location))
  1586. (defun ycmd--get-line-from-location (location)
  1587. "Return line from LOCATION."
  1588. (let-alist location
  1589. (--when-let (and .filepath (find-file-noselect .filepath))
  1590. (with-current-buffer it
  1591. (goto-char (ycmd--col-line-to-position
  1592. .column_num .line_num))
  1593. (back-to-indentation)
  1594. (buffer-substring (point) (line-end-position))))))
  1595. (defun ycmd--view-insert-location (location-group mode)
  1596. "Insert LOCATION-GROUP into `current-buffer' and fontify according MODE.
  1597. LOCATION-GROUP is a cons cell whose car is the filepath and the whose
  1598. cdr is a list of location objects."
  1599. (pcase-let* ((`(,filepath . ,locations) location-group)
  1600. (max-line-num-width
  1601. (cl-loop for location in locations
  1602. maximize (let ((line-num (cdr (assq 'line_num location))))
  1603. (and line-num (length (format "%d" line-num))))))
  1604. (line-num-format (and max-line-num-width
  1605. (format "%%%dd:" max-line-num-width))))
  1606. (insert (propertize (concat filepath "\n") 'face 'bold))
  1607. (mapc (lambda (it)
  1608. (let-alist it
  1609. (when line-num-format
  1610. (insert (format line-num-format .line_num)))
  1611. (insert " ")
  1612. (let ((description (or (and (not (s-blank? .description))
  1613. (s-trim-left .description))
  1614. (ycmd--get-line-from-location it))))
  1615. (ycmd--view-insert-button
  1616. (ycmd--fontify-code (or description "") mode) it))
  1617. (insert "\n")))
  1618. locations)))
  1619. (defvar ycmd-view-mode-map
  1620. (let ((map (make-sparse-keymap)))
  1621. (define-key map (kbd "n") 'next-error-no-select)
  1622. (define-key map (kbd "p") 'previous-error-no-select)
  1623. (define-key map (kbd "q") 'quit-window)
  1624. map))
  1625. (define-derived-mode ycmd-view-mode special-mode "ycmd-view"
  1626. "Major mode for locations view and navigation for `ycmd-mode'.
  1627. \\{ycmd-view-mode-map}"
  1628. (setq next-error-function #'ycmd--next-location))
  1629. (defun ycmd--next-location (num _reset)
  1630. "Navigate to the next location in the view buffer.
  1631. NUM is the number of locations to move forward. If RESET is
  1632. non-nil got to the beginning of buffer before locations
  1633. navigation."
  1634. (forward-button num)
  1635. (ycmd--view-jump-other-window (button-at (point))))
  1636. (define-button-type 'ycmd--error-button
  1637. 'face '(error bold underline)
  1638. 'button 't)
  1639. (define-button-type 'ycmd--warning-button
  1640. 'face '(warning bold underline)
  1641. 'button 't)
  1642. (defun ycmd--make-button (start end type msg)
  1643. "Make a button from START to END of TYPE in the current buffer.
  1644. When clicked, MSG will be shown in the minibuffer."
  1645. (make-text-button
  1646. start end
  1647. 'type type
  1648. 'action (lambda (_) (message msg))))
  1649. (defconst ycmd--file-ready-buttons
  1650. '(("ERROR" . ycmd--error-button)
  1651. ("WARNING" . ycmd--warning-button))
  1652. "A mapping from parse 'kind' to button types.")
  1653. (defun ycmd--line-start-position (line)
  1654. "Find position at the start of LINE."
  1655. (save-excursion
  1656. (ycmd--goto-line line)
  1657. (beginning-of-line)
  1658. (point)))
  1659. (defun ycmd--line-end-position (line)
  1660. "Find position at the end of LINE."
  1661. (save-excursion
  1662. (ycmd--goto-line line)
  1663. (end-of-line)
  1664. (point)))
  1665. (defun ycmd--decorate-single-parse-result (result)
  1666. "Decorates a buffer based on the contents of a single parse RESULT.
  1667. This is a fairly crude form of decoration, but it does give
  1668. reasonable visual feedback on the problems found by ycmd."
  1669. (let-alist result
  1670. (--when-let (find-buffer-visiting .location.filepath)
  1671. (with-current-buffer it
  1672. (let* ((start-pos (ycmd--line-start-position .location.line_num))
  1673. (end-pos (ycmd--line-end-position .location.line_num))
  1674. (btype (cdr (assoc .kind ycmd--file-ready-buttons))))
  1675. (when btype
  1676. (with-silent-modifications
  1677. (ycmd--make-button
  1678. start-pos end-pos
  1679. btype (concat .kind ": " .text
  1680. (when (eq .fixit_available t)
  1681. " (FixIt available)"))))))))))
  1682. (defun ycmd-decorate-with-parse-results (results)
  1683. "Decorates a buffer using the RESULTS of a file-ready parse list.
  1684. This is suitable as an entry in `ycmd-file-parse-result-hook'."
  1685. (with-silent-modifications
  1686. (set-text-properties (point-min) (point-max) nil))
  1687. (mapc 'ycmd--decorate-single-parse-result results)
  1688. results)
  1689. (defun ycmd--display-single-file-parse-result (result)
  1690. "Insert a single file parse RESULT."
  1691. (let-alist result
  1692. (insert (format "%s:%s - %s - %s\n"
  1693. .location.filepath .location.line_num
  1694. .kind .text))))
  1695. (defun ycmd-display-file-parse-results (results)
  1696. "Display parse RESULTS in a buffer."
  1697. (let ((buffer "*ycmd-file-parse-results*"))
  1698. (get-buffer-create buffer)
  1699. (with-current-buffer buffer
  1700. (erase-buffer)
  1701. (mapc 'ycmd--display-single-file-parse-result results))
  1702. (display-buffer buffer)))
  1703. (defun ycmd-parse-buffer ()
  1704. "Parse buffer."
  1705. (interactive)
  1706. (if (not (ycmd-semantic-completer-available-p))
  1707. (message "Native filetype completion not supported for current file, \
  1708. cannot send parse request")
  1709. (when (ycmd--server-alive-p)
  1710. (let ((buffer (current-buffer)))
  1711. (deferred:$
  1712. (deferred:next
  1713. (lambda ()
  1714. (message "Parsing buffer...")
  1715. (ycmd--reset-parse-status)
  1716. (ycmd--conditional-parse)))
  1717. (deferred:nextc it
  1718. (lambda ()
  1719. (with-current-buffer buffer
  1720. (when (eq ycmd--last-status-change 'parsed)
  1721. (message "Parsing buffer done"))))))))))
  1722. (defun ycmd--handle-extra-conf-exception (conf-file)
  1723. "Handle an exception of type `UnknownExtraConf'.
  1724. Handle CONF-FILE according the value of `ycmd-extra-conf-handler'."
  1725. (if (not conf-file)
  1726. (warn "No extra_conf_file included in UnknownExtraConf exception. \
  1727. Consider reporting this.")
  1728. (let ((ignore-p (or (eq ycmd-extra-conf-handler 'ignore)
  1729. (not (y-or-n-p (format "Load YCMD extra conf %s? "
  1730. conf-file))))))
  1731. (ycmd--extra-conf-request conf-file ignore-p)
  1732. (ycmd--reset-parse-status)
  1733. (ycmd-notify-file-ready-to-parse))))
  1734. (defun ycmd--event-notification (event-name &optional extra-data)
  1735. "Send a event notification for EVENT-NAME.
  1736. Optional EXTRA-DATA contains additional data for the request."
  1737. (let ((content (append (list (cons "event_name" event-name))
  1738. (ycmd--get-basic-request-data)
  1739. extra-data)))
  1740. (deferred:try
  1741. (ycmd--request (make-ycmd-request-data
  1742. :handler "event_notification"
  1743. :content content))
  1744. :catch
  1745. (lambda (err)
  1746. (message "Error sending %s request: %s" event-name err)
  1747. (ycmd--report-status 'errored)
  1748. nil))))
  1749. (defun ycmd-notify-file-ready-to-parse ()
  1750. "Send a notification to ycmd that the buffer is ready to be parsed.
  1751. Only one active notification is allowed per buffer, and this
  1752. function enforces that constraint.
  1753. The response of the notification are passed to all of the
  1754. functions in `ycmd-file-parse-result-hook'."
  1755. (when (and ycmd-mode (not (ycmd-parsing-in-progress-p)))
  1756. (ycmd-with-handled-server-exceptions
  1757. (let ((extra-data
  1758. (append (--when-let (and ycmd-tag-files
  1759. (ycmd--get-tag-files request-buffer))
  1760. (list (cons "tag_files" it)))
  1761. (--when-let (and ycmd-seed-identifiers-with-keywords
  1762. (ycmd--get-keywords request-buffer))
  1763. (list (cons "syntax_keywords" it))))))
  1764. (ycmd--report-status 'parsing)
  1765. (ycmd--event-notification "FileReadyToParse" extra-data))
  1766. :bind-current-buffer t
  1767. :on-exception-form (ycmd--report-status 'errored)
  1768. (ycmd--report-status 'parsed)
  1769. (run-hook-with-args 'ycmd-file-parse-result-hook response))))
  1770. (defun ycmd-major-mode-to-file-types (mode)
  1771. "Map a major mode MODE to a list of file-types suitable for ycmd.
  1772. If there is no established mapping, return nil."
  1773. (cdr (assq mode ycmd-file-type-map)))
  1774. (defun ycmd--on-server-timeout ()
  1775. "Kill server process due to timeout."
  1776. (ycmd-close 'errored)
  1777. (message "ERROR: Ycmd server timeout. If this happens regularly you may need to increase `ycmd-startup-timeout'."))
  1778. (defun ycmd--start-server-timeout-timer ()
  1779. "Start the server timeout timer."
  1780. (ycmd--kill-timer ycmd--server-timeout-timer)
  1781. (setq ycmd--server-timeout-timer
  1782. (run-with-timer
  1783. ycmd-startup-timeout nil
  1784. #'ycmd--on-server-timeout)))
  1785. (defun ycmd--start-keepalive-timer ()
  1786. "Kill any existing keepalive timer and start a new one."
  1787. (ycmd--kill-timer ycmd--keepalive-timer)
  1788. (setq ycmd--keepalive-timer
  1789. (run-with-timer
  1790. ycmd-keepalive-period
  1791. ycmd-keepalive-period
  1792. #'ycmd--keepalive)))
  1793. (defun ycmd--generate-hmac-secret ()
  1794. "Generate a new, random 16-byte HMAC secret key."
  1795. (let ((result '()))
  1796. (dotimes (_ 16 result)
  1797. (setq result (cons (byte-to-string (random 256)) result)))
  1798. (apply 'concat result)))
  1799. (defun ycmd--json-encode (obj)
  1800. "Encode a json object OBJ.
  1801. A version of json-encode that uses {} instead of null for nil values.
  1802. This produces output for empty alists that ycmd expects."
  1803. (cl-letf (((symbol-function 'json-encode-keyword)
  1804. (lambda (k) (cond ((eq k t) "true")
  1805. ((eq k json-false) "false")
  1806. ((eq k json-null) "{}")))))
  1807. (json-encode obj)))
  1808. ;; This defines 'ycmd--hmac-function which we use to combine an HMAC
  1809. ;; key and message contents.
  1810. (defun ycmd--secure-hash (x)
  1811. "Generate secure sha256 hash of X."
  1812. (secure-hash 'sha256 x nil nil 1))
  1813. (define-hmac-function ycmd--hmac-function
  1814. ycmd--secure-hash 64 64)
  1815. (defun ycmd--options-contents (hmac-secret)
  1816. "Return a struct with ycmd options and the HMAC-SECRET applied.
  1817. The struct can be json encoded into a file to create a ycmd
  1818. options file.
  1819. When we start a new ycmd server, it needs an options file. It
  1820. reads this file and then deletes it since it contains a secret
  1821. key. So we need to generate a new options file for each ycmd
  1822. instance. This function effectively produces the contents of that
  1823. file."
  1824. (let ((hmac-secret (base64-encode-string hmac-secret))
  1825. (global-config (or ycmd-global-config ""))
  1826. (extra-conf-whitelist (or ycmd-extra-conf-whitelist []))
  1827. (confirm-extra-conf (if (eq ycmd-extra-conf-handler 'load) 0 1))
  1828. (gocode-binary-path (or ycmd-gocode-binary-path ""))
  1829. (godef-binary-path (or ycmd-godef-binary-path ""))
  1830. (rust-src-path (or ycmd-rust-src-path ""))
  1831. (swift-src-path (or ycmd-swift-src-path ""))
  1832. (racerd-binary-path (or ycmd-racerd-binary-path ""))
  1833. (python-binary-path (or ycmd-python-binary-path ""))
  1834. (auto-trigger (if ycmd-auto-trigger-semantic-completion 1 0)))
  1835. `((filepath_completion_use_working_dir . 0)
  1836. (auto_trigger . ,auto-trigger)
  1837. (min_num_of_chars_for_completion . ,ycmd-min-num-chars-for-completion)
  1838. (min_num_identifier_candidate_chars . 0)
  1839. (semantic_triggers . ())
  1840. (filetype_specific_completion_to_disable (gitcommit . 1))
  1841. (collect_identifiers_from_comments_and_strings . 0)
  1842. (max_num_identifier_candidates . ,ycmd-max-num-identifier-candidates)
  1843. (extra_conf_globlist . ,extra-conf-whitelist)
  1844. (global_ycm_extra_conf . ,global-config)
  1845. (confirm_extra_conf . ,confirm-extra-conf)
  1846. (max_diagnostics_to_display . 30)
  1847. (auto_start_csharp_server . 1)
  1848. (auto_stop_csharp_server . 1)
  1849. (use_ultisnips_completer . 1)
  1850. (csharp_server_port . 0)
  1851. (hmac_secret . ,hmac-secret)
  1852. (server_keep_logfiles . 1)
  1853. (gocode_binary_path . ,gocode-binary-path)
  1854. (godef_binary_path . ,godef-binary-path)
  1855. (rust_src_path . ,rust-src-path)
  1856. (swift_src_path . ,swift-src-path)
  1857. (racerd_binary_path . ,racerd-binary-path)
  1858. (python_binary_path . ,python-binary-path))))
  1859. (defun ycmd--create-options-file (hmac-secret)
  1860. "Create a new options file for a ycmd server with HMAC-SECRET.
  1861. This creates a new tempfile and fills it with options. Returns
  1862. the name of the newly created file."
  1863. (let ((options-file (make-temp-file "ycmd-options"))
  1864. (options (ycmd--options-contents hmac-secret)))
  1865. (with-temp-file options-file
  1866. (insert (ycmd--json-encode options)))
  1867. options-file))
  1868. (defun ycmd--exit-code-as-string (code)
  1869. "Return exit status message for CODE."
  1870. (pcase code
  1871. (`3 "unexpected error while loading ycm_core.")
  1872. (`4 (concat "ycm_core library not detected; "
  1873. "you need to compile it by running the "
  1874. "build.py script. See the documentation "
  1875. "for more details."))
  1876. (`5 (concat "ycm_core library compiled for Python 2 "
  1877. "but loaded in Python 3."))
  1878. (`6 (concat "ycm_core library compiled for Python 3 "
  1879. "but loaded in Python 2."))
  1880. (`7 (concat "ycm_core library too old; "
  1881. "PLEASE RECOMPILE by running the build.py "
  1882. "script. See the documentation for more details."))))
  1883. (defun ycmd--server-process-sentinel (process event)
  1884. "Handle Ycmd server PROCESS EVENT."
  1885. (when (memq (process-status process) '(exit signal))
  1886. (let* ((code (process-exit-status process))
  1887. (status (if (eq code 0) 'stopped 'errored)))
  1888. (when (eq status 'errored)
  1889. (--if-let (and (eq (process-status process) 'exit)
  1890. (ycmd--exit-code-as-string code))
  1891. (message "Ycmd server error: %s" it)
  1892. (message "Ycmd server %s" (s-replace "\n" "" event))))
  1893. (ycmd--with-all-ycmd-buffers
  1894. (ycmd--report-status status))
  1895. (ycmd--kill-timer ycmd--keepalive-timer))))
  1896. (defun ycmd--server-process-filter (process string)
  1897. "Filter function for the Ycmd server PROCESS output STRING."
  1898. ;; insert string into process-buffer
  1899. (when (buffer-live-p (process-buffer process))
  1900. (with-current-buffer (process-buffer process)
  1901. (let ((moving (= (point) (process-mark process))))
  1902. (save-excursion
  1903. (goto-char (process-mark process))
  1904. (let ((inhibit-read-only t))
  1905. (insert-before-markers string))
  1906. (set-marker (process-mark process) (point)))
  1907. (when moving (goto-char (process-mark process))))))
  1908. ;; parse port from server output
  1909. (when (and (not ycmd--server-actual-port)
  1910. (string-match "^serving on http://.*:\\\([0-9]+\\\)$"
  1911. string))
  1912. (ycmd--kill-timer ycmd--server-timeout-timer)
  1913. (setq ycmd--server-actual-port
  1914. (string-to-number (match-string 1 string)))
  1915. (ycmd--with-all-ycmd-buffers
  1916. (ycmd--reset-parse-status))
  1917. (ycmd--perform-deferred-parse)))
  1918. (defun ycmd--get-process-environment ()
  1919. "Return `process-evironment'.
  1920. If `ycmd-bypass-url-proxy-services' is non-nil, prepend
  1921. `no_proxy' variable to environment."
  1922. (or ycmd--process-environment
  1923. (setq ycmd--process-environment
  1924. (append (and ycmd-bypass-url-proxy-services
  1925. (not (or (getenv "NO_PROXY")
  1926. (getenv "no_PROXY")
  1927. (getenv "no_proxy")))
  1928. (list (concat "NO_PROXY=" ycmd-host)))
  1929. process-environment))))
  1930. (defun ycmd--start-server ()
  1931. "Start a new server and return the process."
  1932. (unless ycmd-server-command
  1933. (user-error "Error: The variable `ycmd-server-command' is not set. \
  1934. See the docstring of the variable for an example"))
  1935. (let ((proc-buff (get-buffer-create ycmd--server-buffer-name)))
  1936. (with-current-buffer proc-buff
  1937. (setq buffer-read-only t)
  1938. (let ((inhibit-read-only t))
  1939. (buffer-disable-undo)
  1940. (erase-buffer)))
  1941. (setq ycmd--process-environment nil)
  1942. (let* ((port (and (numberp ycmd-server-port)
  1943. (> ycmd-server-port 0)
  1944. ycmd-server-port))
  1945. (hmac-secret (ycmd--generate-hmac-secret))
  1946. (options-file (ycmd--create-options-file hmac-secret))
  1947. (args (append (and port (list (format "--port=%d" port)))
  1948. (list (concat "--options_file=" options-file))
  1949. ycmd-server-args))
  1950. (server-program+args (append ycmd-server-command args))
  1951. (process-environment (ycmd--get-process-environment))
  1952. (proc (apply #'start-process ycmd--server-process-name proc-buff
  1953. server-program+args)))
  1954. (ycmd--with-all-ycmd-buffers
  1955. (ycmd--report-status 'starting))
  1956. (setq ycmd--server-actual-port nil
  1957. ycmd--hmac-secret hmac-secret)
  1958. (set-process-query-on-exit-flag proc nil)
  1959. (set-process-sentinel proc #'ycmd--server-process-sentinel)
  1960. (set-process-filter proc #'ycmd--server-process-filter)
  1961. proc)))
  1962. (defun ycmd-wait-until-server-is-ready (&optional include-subserver)
  1963. "Wait until server is ready.
  1964. If INCLUDE-SUBSERVER is non-nil wait until subserver is ready.
  1965. Return t when server is ready. Signal error in case of timeout.
  1966. The timeout can be set with the variable
  1967. `ycmd-startup-timeout'."
  1968. (catch 'ready
  1969. (let ((server-start-time (float-time)))
  1970. (ycmd--kill-timer ycmd--server-timeout-timer)
  1971. (while (ycmd-running-p)
  1972. (sit-for 0.1)
  1973. (if (ycmd--server-ready-p include-subserver)
  1974. (progn
  1975. (ycmd--with-all-ycmd-buffers
  1976. (ycmd--reset-parse-status))
  1977. (throw 'ready t))
  1978. ;; timeout after specified period
  1979. (when (< ycmd-startup-timeout
  1980. (- (float-time) server-start-time))
  1981. (ycmd--on-server-timeout)))))))
  1982. (defun ycmd--column-in-bytes ()
  1983. "Calculate column offset in bytes for the current position and buffer."
  1984. (- (position-bytes (point))
  1985. (position-bytes (line-beginning-position))))
  1986. ;; https://github.com/abingham/emacs-ycmd/issues/165
  1987. (eval-and-compile
  1988. (if (version-list-< (version-to-list emacs-version) '(25))
  1989. (defun ycmd--encode-string (s) s)
  1990. (defun ycmd--encode-string (s) (encode-coding-string s 'utf-8 t))))
  1991. (defun ycmd--get-basic-request-data ()
  1992. "Build the basic request data alist for a server request."
  1993. (let* ((column-num (+ 1 (ycmd--column-in-bytes)))
  1994. (line-num (line-number-at-pos (point)))
  1995. (full-path (ycmd--encode-string (or (buffer-file-name) "")))
  1996. (file-contents (ycmd--encode-string
  1997. (buffer-substring-no-properties
  1998. (point-min) (point-max))))
  1999. (file-types (or (ycmd-major-mode-to-file-types major-mode)
  2000. '("generic"))))
  2001. `(("file_data" .
  2002. ((,full-path . (("contents" . ,file-contents)
  2003. ("filetypes" . ,file-types)))))
  2004. ("filepath" . ,full-path)
  2005. ("line_num" . ,line-num)
  2006. ("column_num" . ,column-num))))
  2007. (defvar ycmd--log-enabled nil
  2008. "If non-nil, http content will be logged.
  2009. This is useful for debugging.")
  2010. (defun ycmd-toggle-log-enabled ()
  2011. "Toggle `ycmd--log-enabled' variable."
  2012. (interactive)
  2013. (let ((log-enabled (not ycmd--log-enabled)))
  2014. (message "Ycmd Log %s" (if log-enabled "enabled" "disabled"))
  2015. (setq ycmd--log-enabled log-enabled)))
  2016. (defun ycmd--log-content (header content)
  2017. "Insert log with HEADER and CONTENT in a buffer."
  2018. (when ycmd--log-enabled
  2019. (let ((buffer (get-buffer-create "*ycmd-content-log*")))
  2020. (with-current-buffer buffer
  2021. (save-excursion
  2022. (goto-char (point-max))
  2023. (insert (format "\n%s\n\n" header))
  2024. (insert (pp-to-string content)))))))
  2025. (defun ycmd-show-debug-info ()
  2026. "Show debug information."
  2027. (interactive)
  2028. (let ((data (make-ycmd-request-data :handler "debug_info"))
  2029. (buffer (current-buffer)))
  2030. (with-help-window (get-buffer-create " *ycmd-debug-info*")
  2031. (with-current-buffer standard-output
  2032. (princ "Ycmd debug information for buffer ")
  2033. (insert (propertize (buffer-name buffer) 'face 'bold))
  2034. (princ " in ")
  2035. (let ((mode (buffer-local-value 'major-mode buffer)))
  2036. (insert-button (symbol-name mode)
  2037. 'type 'help-function
  2038. 'help-args (list mode)))
  2039. (princ ":\n\n")
  2040. (--if-let (and (ycmd--server-alive-p)
  2041. (ycmd-deferred:sync!
  2042. (ycmd-with-handled-server-exceptions
  2043. (ycmd--request data))))
  2044. (pp it)
  2045. (princ "No debug info available from server"))
  2046. (princ "\n\n")
  2047. (princ "Server is ")
  2048. (let ((running (ycmd--server-alive-p)))
  2049. (insert (propertize (if running "running" "not running")
  2050. 'face (if running 'success '(warning bold))))
  2051. (when running
  2052. (insert
  2053. (format " at: %s:%d" ycmd-host ycmd--server-actual-port))))
  2054. (princ "\n\n")
  2055. (princ "Ycmd Mode is ")
  2056. (let ((enabled (buffer-local-value 'ycmd-mode buffer)))
  2057. (insert (propertize (if enabled "enabled" "disabled")
  2058. 'face (if enabled 'success '(warning bold)))))
  2059. (save-excursion
  2060. (let ((end (point)))
  2061. (backward-paragraph)
  2062. (fill-region-as-paragraph (point) end)))
  2063. (princ "\n\n--------------------\n\n")
  2064. (princ (format "Ycmd version: %s\n" (ignore-errors (ycmd-version))))
  2065. (princ (format "Emacs version: %s\n" emacs-version))
  2066. (princ (format "System: %s\n" system-configuration))
  2067. (princ (format "Window system: %S\n" window-system))))))
  2068. (defun ycmd-filter-and-sort-candidates (request-data)
  2069. "Use ycmd to filter and sort identifiers from REQUEST-DATA.
  2070. This request allows to use ycmd's filtering and sorting
  2071. mechanism on arbitrary sets of identifiers.
  2072. The request data should be something like:
  2073. \((candidates \"candidate1\" \"candidate2\")
  2074. (sort_property . \"\")
  2075. (query . \"cand\"))
  2076. If candidates is a list with identifiers, sort_property should be
  2077. and empty string, however when candidates is a more complex
  2078. structure it is used to specify the sort key."
  2079. (let ((data (make-ycmd-request-data
  2080. :handler "filter_and_sort_candidates"
  2081. :content request-data)))
  2082. (ycmd-deferred:sync!
  2083. (ycmd-with-handled-server-exceptions (ycmd--request data)))))
  2084. (defun ycmd--send-completer-available-request (&optional mode)
  2085. "Send request to check if a semantic completer exists for MODE.
  2086. Response is non-nil if semantic complettion is available."
  2087. (let ((data (make-ycmd-request-data
  2088. :handler "semantic_completion_available")))
  2089. (when mode
  2090. (let* ((buffer (current-buffer))
  2091. (full-path (ycmd--encode-string (or (buffer-file-name buffer) "")))
  2092. (content (ycmd-request-data-content data))
  2093. (file-types (assoc "filetypes"
  2094. (assoc full-path
  2095. (assoc "file_data" content)))))
  2096. (when (consp file-types)
  2097. (setcdr file-types (ycmd-major-mode-to-file-types mode)))))
  2098. (ycmd-deferred:sync!
  2099. (ycmd-with-handled-server-exceptions (ycmd--request data)))))
  2100. (defun ycmd-semantic-completer-available-p ()
  2101. "Return t if a semantic completer is available for current `major-mode'."
  2102. (let ((mode major-mode))
  2103. (or (gethash mode ycmd--available-completers)
  2104. (--when-let (ycmd--send-completer-available-request mode)
  2105. (puthash mode (or (eq it t) 'none) ycmd--available-completers)))))
  2106. (defun ycmd--get-request-hmac (method path body)
  2107. "Generate HMAC for request from METHOD, PATH and BODY."
  2108. (ycmd--hmac-function
  2109. (mapconcat (lambda (val)
  2110. (ycmd--hmac-function
  2111. (ycmd--encode-string val) ycmd--hmac-secret))
  2112. `(,method ,path ,(or body "")) "")
  2113. ycmd--hmac-secret))
  2114. (cl-defstruct ycmd-request-data
  2115. "Structure for storing the ycmd server request data.
  2116. Slots:
  2117. `handler'
  2118. Specifies the the path portion of the URL. For example, if
  2119. HANDLER is 'feed_llama', the request URL is
  2120. 'http://host:port/feed_llama'.
  2121. `content'
  2122. An alist that will be JSON-encoded and sent over at the
  2123. content of the HTTP message."
  2124. handler
  2125. (content (ycmd--get-basic-request-data)))
  2126. (cl-defun ycmd--request (request-data
  2127. &key
  2128. (type "POST")
  2129. (params nil))
  2130. "Send an asynchronous HTTP request to the ycmd server.
  2131. This starts the server if necessary.
  2132. Returns a deferred object which resolves to the content of the
  2133. response message.
  2134. REQUEST-DATA is a `ycmd-request-data' structure.
  2135. PARSER specifies the function that will be used to parse the
  2136. response to the message. Typical values are buffer-string and
  2137. json-read. This function will be passed an the completely
  2138. unmodified contents of the response (i.e. not JSON-decoded or
  2139. anything like that)."
  2140. (unless (ycmd--server-alive-p)
  2141. (message "Ycmd server is not running. Can't send `%s' request!"
  2142. (ycmd-request-data-handler request-data))
  2143. (cl-return-from ycmd--request (deferred:next)))
  2144. (let* ((url-show-status (not ycmd-hide-url-status))
  2145. (url-proxy-services (unless ycmd-bypass-url-proxy-services
  2146. url-proxy-services))
  2147. (process-environment (ycmd--get-process-environment))
  2148. (path (concat "/" (ycmd-request-data-handler request-data)))
  2149. (content (json-encode (ycmd-request-data-content request-data)))
  2150. (hmac (ycmd--get-request-hmac type path content))
  2151. (encoded-hmac (base64-encode-string hmac 't))
  2152. (url (format "http://%s:%s%s"
  2153. ycmd-host ycmd--server-actual-port path))
  2154. (headers `(("Content-Type" . "application/json")
  2155. ("X-Ycm-Hmac" . ,encoded-hmac)))
  2156. (parser (lambda ()
  2157. (let ((json-array-type 'list))
  2158. (json-read)))))
  2159. (ycmd--log-content "HTTP REQUEST CONTENT" content)
  2160. (deferred:$
  2161. (request-deferred url :type type :params params :data content
  2162. :parser parser :headers headers)
  2163. (deferred:nextc it
  2164. (lambda (response)
  2165. (let ((data (request-response-data response)))
  2166. (ycmd--log-content "HTTP RESPONSE CONTENT" data)
  2167. data))))))
  2168. (provide 'ycmd)
  2169. ;;; ycmd.el ends here
  2170. ;; Local Variables:
  2171. ;; indent-tabs-mode: nil
  2172. ;; End: