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

1923 lines
71 KiB

  1. ;;; meghanada.el --- A better java development mode -*- coding: utf-8; lexical-binding: t; -*-
  2. ;; Copyright (C) 2016 - 2020 Yutaka Matsubara
  3. ;; License: http://www.gnu.org/licenses/gpl.html
  4. ;; Author: Yutaka Matsubara (yutaka.matsubara@gmail.com)
  5. ;; Homepage: https://github.com/mopemope/meghanada-emacs
  6. ;; Keywords: languages java
  7. ;; Package-Version: 1.3.1
  8. ;; Package-Requires: ((emacs "24.3") (yasnippet "0.6.1") (company "0.9.0") (flycheck "0.23"))
  9. ;; This program is free software; you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation, either version 3 of the License, or
  12. ;; (at your option) any later version.
  13. ;; This program is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. ;;; Commentary:
  20. ;;
  21. ;; Meghanada has a server component which can read the AST of your
  22. ;; project and its dependencies, providing features.
  23. ;;
  24. ;;
  25. ;;; Code:
  26. (require 'cl-lib)
  27. (require 'pcase)
  28. (require 'thingatpt)
  29. (require 'compile)
  30. (require 'imenu)
  31. (require 'url)
  32. (require 'which-func)
  33. (require 'xref)
  34. (autoload 'meghanada-company-enable "company-meghanada")
  35. (autoload 'meghanada-flycheck-enable "flycheck-meghanada")
  36. (autoload 'meghanada-eldoc-enable "eldoc-meghanada")
  37. ;;
  38. ;; Const
  39. ;;
  40. (defconst meghanada-version "1.3.1")
  41. (defconst meghanada-setup-version "0.0.2")
  42. (defconst meghanada--eot "\n;;EOT\n")
  43. (defconst meghanada--junit-buf-name "*meghanada-junit*")
  44. (defconst meghanada--task-buf-name "*meghanada-task*")
  45. (defconst meghanada--ref-buf-name "*meghanada-reference*")
  46. (defconst meghanada--search-buf-name "*meghanada-search-everywhere*")
  47. (defconst meghanada--show-project-buf-name "*meghanada-project*")
  48. (defconst meghanada--typeinfo-buf-name "*meghanada-typeinfo*")
  49. (defconst meghanada--install-err-buf-name "*meghanada-install-error*")
  50. (defconst meghanada--err-buf-name "*meghanada-error*")
  51. ;;
  52. ;; Customizable variables
  53. ;;
  54. (defgroup meghanada nil
  55. "Java minor mode powered by meghanada."
  56. :group 'java)
  57. (defcustom meghanada-host "127.0.0.1"
  58. "Meghanada server host address."
  59. :group 'meghanada
  60. :type 'string)
  61. (defcustom meghanada-port 0
  62. "Meghanada server port.
  63. A port number of 0 means that the port number is automatically allocated, typically from an ephemeral port range.
  64. default 0.
  65. "
  66. :group 'meghanada
  67. :type 'integer)
  68. (defcustom meghanada-debug nil
  69. "If true, meghanada-server outputs debug log."
  70. :group 'meghanada
  71. :type 'boolean)
  72. (defcustom meghanada-use-company t
  73. "If true, company-mode auto-comletion is enabled."
  74. :group 'meghanada
  75. :type 'boolean)
  76. (defcustom meghanada-use-flycheck t
  77. "If true, diagnostics report with flyecheck is enabled."
  78. :group 'meghanada
  79. :type 'boolean)
  80. (defcustom meghanada-use-eldoc t
  81. "If true, eldoc for meghanada enabled."
  82. :group 'meghanada
  83. :type 'boolean)
  84. (defcustom meghanada-auto-start t
  85. "If true, meghanada-server start automatically."
  86. :group 'meghanada
  87. :type 'boolean)
  88. (defcustom meghanada-server-remote-debug nil
  89. "If true, meghanda-server enabled remote debug."
  90. :group 'meghanada
  91. :type 'boolean)
  92. (defcustom meghanada-server-install-dir (locate-user-emacs-file "meghanada/")
  93. "Install directory for meghanada-server.
  94. The slash is expected at the end."
  95. :group 'meghanada
  96. :risky t
  97. :type 'directory)
  98. (defcustom meghanada-java-path "java"
  99. "Path of the java executable.
  100. If the path of maven is in the environment, we can use the short path.
  101. Otherwise, we can always use the full path, in Windows, we can use this to get it:
  102. `(expand-file-name \"bin/java.exe\" (getenv \"JAVA_HOME\"))'
  103. In linux or macOS, it can be \"java\". In Windows, it can be \"java.exe\"."
  104. :group 'meghanada
  105. :type 'string)
  106. (defcustom meghanada-maven-path "mvn"
  107. "Path of the maven executable.
  108. If the path of maven is in the environment, we can use the short path.
  109. Otherwise, we can always use the full path.
  110. For the short paht:
  111. In linux or macOS, it can be \"mvn\"; In Windows, it can be \"mvn.cmd\". "
  112. :group 'meghanada
  113. :type 'string)
  114. (defcustom meghanada-maven-local-repository nil
  115. "Overriding maven repository path."
  116. :group 'meghanada
  117. :type 'string)
  118. (defcustom meghanada-javac-xlint "-Xlint:all"
  119. "Overriding javac's -Xlint."
  120. :group 'meghanada
  121. :type 'string)
  122. (defcustom meghanada-gradle-version nil
  123. "Overriding gradle version."
  124. :group 'meghanada
  125. :type 'string)
  126. (defcustom meghanada-gradle-prepare-compile-task nil
  127. "Set to gradle prepare compileJava task name."
  128. :group 'meghanada
  129. :type 'string)
  130. (defcustom meghanada-gradle-prepare-test-compile-task nil
  131. "Set to gradle prepare compileTestJava task name."
  132. :group 'meghanada
  133. :type 'string)
  134. (defcustom meghanada-skip-build-subprojects t
  135. "If true, skip gradle dependency subprojects build."
  136. :group 'meghanada
  137. :type 'boolean)
  138. (defcustom meghanada-jvm-option nil
  139. "Set to all meghanada java process jvm option.
  140. Example. (setq meghanada-jvm-option \"-Dhttp.proxyHost=test.proxy.com -Dhttp.proxyPort=8080\")
  141. "
  142. :group 'meghanada
  143. :type 'string)
  144. (defcustom meghanada-server-jvm-option "-Xms128m -XX:ReservedCodeCacheSize=240m -XX:SoftRefLRUPolicyMSPerMB=50 -ea -Dsun.io.useCanonCaches=false"
  145. "Set to meghanada server process jvm option."
  146. :group 'meghanada
  147. :type 'string)
  148. (defcustom meghanada-mode-key-prefix [?\C-c]
  149. "The prefix key for meghanada-mode commands."
  150. :group 'meghanada
  151. :type 'sexp)
  152. (defcustom meghanada-reference-prepare #'meghanada--reference-prepare
  153. "It is called before meghanada-reference."
  154. :group 'meghanada
  155. :type 'function)
  156. (defcustom meghanada-reference-callback #'meghanada--reference-callback
  157. "It will be called after receiving meghanada-reference result."
  158. :group 'meghanada
  159. :type 'function)
  160. (defcustom meghanada-typeinfo-prepare #'meghanada--typeinfo-prepare
  161. "It is called before meghanada-typeinfo."
  162. :group 'meghanada
  163. :type 'function)
  164. (defcustom meghanada-typeinfo-callback #'meghanada--typeinfo-callback
  165. "It will be called after receiving meghanada-typeinfo result."
  166. :group 'meghanada
  167. :type 'function)
  168. (defcustom meghanada-search-prepare #'meghanada--search-prepare
  169. "It is called before meghanada-search-everywhere."
  170. :group 'meghanada
  171. :type 'function)
  172. (defcustom meghanada-search-callback #'meghanada--search-callback
  173. "It will be called after receiving meghanada-search-everywhere result."
  174. :group 'meghanada
  175. :type 'function)
  176. (defcustom meghanada-cache-in-project nil
  177. "If true, create a cache in the project.otherwise, create in cache root directory (~/.cache/meghanada). default nil"
  178. :group 'meghanada
  179. :type 'boolean)
  180. (defcustom meghanada-cache-root nil
  181. "Set to meghanada cache root.default value is '~/.cache/meghanada'."
  182. :group 'meghanada
  183. :type 'string)
  184. (defcustom meghanada-task-buffer-auto-scroll t
  185. "If true, automatically move to the end of the task buffer after inserting new output."
  186. :group 'meghanada
  187. :type 'boolean)
  188. (defcustom meghanada-make-search-query #'meghanada--make-search-query
  189. "Sets the function that returns the search query."
  190. :group 'meghanada
  191. :type 'function)
  192. (defcustom meghanada-import-static-enable "java.util.Objects,org.junit.Assert"
  193. "Sets import static completion classes."
  194. :group 'meghanada
  195. :type 'string)
  196. (defcustom meghanada-full-text-search-enable nil
  197. "If true, Enable full text search and meghanada-search-everywhere."
  198. :group 'meghanada
  199. :type 'boolean)
  200. (defcustom meghanada-completion-matcher "prefix"
  201. "Select completion matcher. You can choose from prefix, contains, fuzzy, came-case. default is prefix."
  202. :group 'meghanada
  203. :type 'string)
  204. (defcustom meghanada-class-completion-matcher "prefix"
  205. "Select class completion matcher. You can choose from prefix, contains, fuzzy, came-case. default is prefix."
  206. :group 'meghanada
  207. :type 'string)
  208. (defcustom meghanada-mode-after-test-hook '()
  209. "Hook that is called after a JUnit test execution is done."
  210. :group 'meghanada)
  211. (defcustom meghanada-telemetry-enable nil
  212. "If true, Enables telemetry and allows you to collect and submit performance data."
  213. :group 'meghanada
  214. :type 'boolean)
  215. ;;
  216. ;; utility
  217. ;;
  218. (defun meghanada--what-line ()
  219. "Return the current buffer line number and narrowed line number of point."
  220. (let ((start (point-min))
  221. (n (line-number-at-pos)))
  222. (if (= start 1)
  223. n
  224. (save-excursion
  225. (save-restriction
  226. (widen)
  227. (+ n (line-number-at-pos start) -1) n)))))
  228. (defun meghanada--real-current-column ()
  229. "like `current-column', but skip invisible characters in pretty-symbol-mode."
  230. (- (point) (line-beginning-position)))
  231. (defun meghanada--what-column ()
  232. "Returns the current column number."
  233. (number-to-string (1+ (meghanada--real-current-column))))
  234. (defun meghanada--what-symbol ()
  235. "Returns the symbol on the cursor."
  236. (thing-at-point 'symbol))
  237. (defun meghanada--what-word ()
  238. "Returns the word on the cursor."
  239. (thing-at-point 'word))
  240. (defmacro meghanada--without-narrowing (&rest body)
  241. (declare (indent 0) (debug t))
  242. `(save-restriction
  243. (widen)
  244. (progn ,@body)))
  245. (defun meghanada--remove-eot (out)
  246. (replace-regexp-in-string meghanada--eot "" out))
  247. (defun meghanada--goto-line (line)
  248. (goto-char (point-min))
  249. (forward-line (1- line)))
  250. (defun meghanada--line-column-to-point (line column)
  251. (save-excursion
  252. (meghanada--goto-line line)
  253. (forward-char (1- column))
  254. (point)))
  255. (defun meghanada--delete-whole-line (&optional arg)
  256. "Delete the current line without putting it in the `kill-ring'.
  257. Derived from function `kill-whole-line'. ARG is defined as for that
  258. function."
  259. (setq arg (or arg 1))
  260. (if (and (> arg 0)
  261. (eobp)
  262. (save-excursion (forward-visible-line 0) (eobp)))
  263. (signal 'end-of-buffer nil))
  264. (if (and (< arg 0)
  265. (bobp)
  266. (save-excursion (end-of-visible-line) (bobp)))
  267. (signal 'beginning-of-buffer nil))
  268. (cond ((zerop arg)
  269. (delete-region (progn (forward-visible-line 0) (point))
  270. (progn (end-of-visible-line) (point))))
  271. ((< arg 0)
  272. (delete-region (progn (end-of-visible-line) (point))
  273. (progn (forward-visible-line (1+ arg))
  274. (unless (bobp)
  275. (backward-char))
  276. (point))))
  277. (t
  278. (delete-region (progn (forward-visible-line 0) (point))
  279. (progn (forward-visible-line arg) (point))))))
  280. (defun meghanada-version ()
  281. "Show meghanada-version ."
  282. (interactive)
  283. (message meghanada-version))
  284. ;;
  285. ;; meghanada-server process management.
  286. ;;
  287. (defvar meghanada--server-process nil)
  288. (defvar meghanada--server-buffer "*meghanada-server-log*")
  289. (defvar meghanada--server-pending nil)
  290. (defvar meghanada--server-jar nil)
  291. ;;
  292. ;; meghanada-client process management.
  293. ;;
  294. (defvar meghanada--client-process nil)
  295. (defvar meghanada--client-buffer "*meghanada-client*")
  296. (defvar meghanada--connect-host meghanada-host)
  297. (defvar meghanada--connect-port meghanada-port)
  298. (defvar meghanada--client-pending nil)
  299. (defvar meghanada--task-client-process nil)
  300. (defvar meghanada--task-buffer nil)
  301. ;; TODO pop-to-buffer
  302. (defun meghanada--download-from-url (url dest-jar)
  303. "Download a jar file from URL to DEST-JAR Path."
  304. (let ((dest meghanada-server-install-dir))
  305. (unless (file-exists-p dest)
  306. (make-directory dest t))
  307. (message (format "Download module from %s. Please wait ..." url))
  308. (url-handler-mode t)
  309. (if (file-exists-p url)
  310. (progn
  311. (url-copy-file url dest-jar)
  312. (message (format "Downloaded module from %s to %s." url dest-jar)))
  313. (error "Not found %s" url))))
  314. (defun meghanada--setup ()
  315. "Setup meghanada-server-module."
  316. (meghanada--download-setup-jar)
  317. (meghanada--run-setup))
  318. (defun meghanada--setup-options ()
  319. (let ((options '()))
  320. (when meghanada-jvm-option
  321. (push meghanada-jvm-option options))
  322. (mapconcat 'identity options " ")))
  323. (defun meghanada--run-setup ()
  324. "Setup meghanada server module."
  325. (let ((jar (meghanada--locate-setup-jar))
  326. (dest meghanada-server-install-dir))
  327. (if (file-exists-p jar)
  328. (let ((cmd (format "%s %s -jar %s --dest %s --server-version %s --simple"
  329. (shell-quote-argument meghanada-java-path)
  330. (meghanada--setup-options)
  331. (shell-quote-argument jar)
  332. (expand-file-name dest)
  333. meghanada-version)))
  334. (message "Download meghanada server module. Please wait ...")
  335. (let ((proc (start-process-shell-command "*meghanada-install*" "*meghanada-install*" cmd))
  336. (buf (current-buffer)))
  337. (pop-to-buffer "*meghanada-install*")
  338. (set-process-filter
  339. proc
  340. #'(lambda (process msg)
  341. (when (process-live-p process)
  342. (with-current-buffer (process-buffer process)
  343. (insert msg)
  344. (message "Download server module ...")))))
  345. (set-process-sentinel
  346. proc
  347. #'(lambda (process msg)
  348. (unless (process-live-p process)
  349. (if (eq 0 (process-exit-status process))
  350. (progn
  351. (message (format "Success. It downloaded to %s." meghanada-server-install-dir))
  352. (pop-to-buffer buf)
  353. (with-current-buffer buf
  354. (meghanada-mode t)
  355. (meghanada-restart)))
  356. (message (format "Failure. The installation seems to have failed, please check the message buffer for details. The jar should have been downloaded to %s." meghanada-server-install-dir)))))))))))
  357. (defun meghanada--download-setup-jar ()
  358. "Download setup-jar file from bintray."
  359. (let ((url (format
  360. "https://dl.bintray.com/mopemope/meghanada/meghanada-setup-%s.jar"
  361. meghanada-setup-version))
  362. (setup-jar (meghanada--locate-setup-jar)))
  363. (unless (file-exists-p setup-jar)
  364. (meghanada--download-from-url
  365. url
  366. setup-jar))))
  367. (defun meghanada--download-server-jar ()
  368. "Direct download server jar file."
  369. (let ((dest meghanada-server-install-dir)
  370. (dest-jar (meghanada--locate-server-jar))
  371. (url (format "https://dl.bintray.com/mopemope/meghanada/meghanada-%s.jar" meghanada-version)))
  372. (unless (file-exists-p dest)
  373. (make-directory dest t))
  374. (message (format "Download server module from %s. Please wait." url))
  375. (url-handler-mode t)
  376. (if (file-exists-p url)
  377. (progn
  378. (url-copy-file url dest-jar)
  379. (message (format "Downloaded server module from %s to %s." url dest-jar)))
  380. (error "Not found %s" url))))
  381. ;;;###autoload
  382. (defun meghanada-install-server ()
  383. "Install meghanada-server's jar file from bintray ."
  384. (interactive)
  385. (let ((dest-jar (meghanada--locate-server-jar)))
  386. (if (file-exists-p dest-jar)
  387. nil
  388. (condition-case err
  389. (progn
  390. (meghanada--setup)
  391. t)
  392. (error
  393. (let ((error-buf meghanada--install-err-buf-name))
  394. (with-current-buffer (get-buffer-create error-buf)
  395. (insert (format "Error: %s" (error-message-string err)))
  396. (compilation-mode))))))))
  397. ;;;###autoload
  398. (defun meghanada-update-server ()
  399. "Update meghanada-server's jar file from bintray ."
  400. (interactive)
  401. (meghanada-install-server))
  402. (defun meghanada--locate-server-jar ()
  403. (expand-file-name
  404. (format "meghanada-%s.jar" meghanada-version)
  405. meghanada-server-install-dir))
  406. (defun meghanada--locate-setup-jar ()
  407. "Return the path of the meghanada-setup.jar file."
  408. (expand-file-name
  409. (format "meghanada-setup-%s.jar" meghanada-setup-version)
  410. meghanada-server-install-dir))
  411. (defun meghanada--server-options ()
  412. (let ((options '()))
  413. (when meghanada-maven-path
  414. (push (format "-Dmeghanada.maven.path=%s" meghanada-maven-path) options))
  415. (when meghanada-maven-local-repository
  416. (push (format "-Dmeghanada.maven.local.repository=%s" meghanada-maven-local-repository) options))
  417. (when meghanada-javac-xlint
  418. (push (format "-Dmeghanada.javac.arg=%s" meghanada-javac-xlint) options))
  419. (when meghanada-import-static-enable
  420. (push (format "-Dmeghanada.search.static.method.classes=%s" meghanada-import-static-enable) options))
  421. (when meghanada-full-text-search-enable
  422. (push "-Dmeghanada.full.text.searchs=true" options))
  423. (when meghanada-gradle-version
  424. (push (format "-Dmeghanada.gradle.version=%s" meghanada-gradle-version) options))
  425. (when meghanada-gradle-prepare-compile-task
  426. (push (format "-Dmeghanada.gradle.prepare.compile.task=%s" meghanada-gradle-prepare-compile-task) options))
  427. (when meghanada-gradle-prepare-test-compile-task
  428. (push (format "-Dmeghanada.gradle.prepare.test.compile.task=%s" meghanada-gradle-prepare-test-compile-task) options))
  429. (if meghanada-skip-build-subprojects
  430. (push "-Dmeghanada.skip.build.subprojects=true" options)
  431. (push "-Dmeghanada.skip.build.subprojects=false" options))
  432. (if meghanada-cache-in-project
  433. (push "-Dmeghanada.cache.in.project=true" options)
  434. (push "-Dmeghanada.cache.in.project=false" options))
  435. (when meghanada-cache-root
  436. (push (format "-Dmeghanada.cache.root=%s" meghanada-cache-root) options))
  437. (when meghanada-server-remote-debug
  438. (push "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" options))
  439. (push "-Dmeghanada.format=sexp" options)
  440. (when meghanada-completion-matcher
  441. (push (format "-Dmeghanada.completion.matcher=%s" meghanada-completion-matcher) options))
  442. (when meghanada-class-completion-matcher
  443. (push (format "-Dmeghanada.class.completion.matcher=%s" meghanada-class-completion-matcher) options))
  444. (when meghanada-jvm-option
  445. (push meghanada-jvm-option options))
  446. (when meghanada-telemetry-enable
  447. (push "-Dmeghanada.telemetry.enable=true" options))
  448. (push "-Djava.net.preferIPv4Stack=true" options)
  449. (mapconcat 'identity options " ")))
  450. (defun meghanada--start-server-process ()
  451. (let ((jar (meghanada--locate-server-jar)))
  452. (if (file-exists-p jar)
  453. (let ((process-connection-type nil)
  454. (process-adaptive-read-buffering nil)
  455. (cmd (format "%s %s %s -Dfile.encoding=UTF-8 -jar %s -p %d %s %s"
  456. (shell-quote-argument meghanada-java-path)
  457. (meghanada--server-options)
  458. meghanada-server-jvm-option
  459. (shell-quote-argument jar)
  460. meghanada-port
  461. (if meghanada-debug "-v" "")
  462. (concat "-l " temporary-file-directory "meghanada_server_" (number-to-string (user-uid)) ".log")))
  463. process)
  464. (message (format "launch server cmd:%s" cmd))
  465. (setq process
  466. (start-process-shell-command
  467. "meghanada-server"
  468. meghanada--server-buffer
  469. cmd))
  470. (buffer-disable-undo meghanada--server-buffer)
  471. (set-process-query-on-exit-flag process nil)
  472. (set-process-sentinel process 'meghanada--server-process-sentinel)
  473. (set-process-filter process 'meghanada--server-process-filter)
  474. (message "Meghanada-Server Starting ...")
  475. process)
  476. (message "%s"
  477. (substitute-command-keys
  478. "Missing server module. Type `\\[meghanada-install-server]' to install meghanada-server")))))
  479. (defun meghanada--get-server-process-create ()
  480. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  481. (progn
  482. (message "already started meghanada-server. see *meghanada-server-log* buffer.")
  483. meghanada--server-process)
  484. (setq meghanada--server-process (meghanada--start-server-process))))
  485. (defun meghanada--server-process-sentinel (process msg)
  486. (unless (process-live-p process)
  487. (set-process-sentinel process 'ignore)
  488. (set-process-filter process 'ignore)
  489. (delete-process process)
  490. (setq meghanada--server-process nil)
  491. (error (format "Error:meghanada-server process stopped: %s. Please check *meghanada-server-log* buffer" msg))))
  492. (defun meghanada--server-process-filter (process output)
  493. (let ((pbuf (process-buffer process)))
  494. (when (buffer-live-p pbuf)
  495. (with-current-buffer pbuf
  496. (let ((moving (= (point) (process-mark process))))
  497. (save-excursion
  498. (goto-char (process-mark process))
  499. (insert output)
  500. (set-marker (process-mark process) (point)))
  501. (if moving
  502. (goto-char (process-mark process)))
  503. (when (string-match "Start server" output)
  504. (string-match "port:\\([0-9]+\\)+" output)
  505. (let* ((p (substring output (match-beginning 1) (match-end 1))))
  506. (setq meghanada--connect-port (string-to-number p)))
  507. (message "Server waiting client connection ...")
  508. (when meghanada--server-pending
  509. (funcall meghanada--server-pending)
  510. (setq meghanada--server-pending nil)))
  511. (when (string-match "MEGHANADA_FAILED " output)
  512. (string-match "FILE:\\(.*.log\\)" output)
  513. (let* ((log-file (substring output (match-beginning 1) (match-end 1))))
  514. (with-help-window (get-buffer-create meghanada--err-buf-name)
  515. (pop-to-buffer meghanada--err-buf-name)
  516. (save-excursion
  517. (insert-file-contents-literally log-file)
  518. (message
  519. (format "ERROR: project initialize error. please check log %s" log-file)))))))))))
  520. ;;;###autoload
  521. (defun meghanada-server-start ()
  522. "Start the meghanada server."
  523. (interactive)
  524. (meghanada--get-server-process-create))
  525. ;;;###autoload
  526. (defun meghanada-server-kill ()
  527. "Kill the meghanada server."
  528. (interactive)
  529. (when (and meghanada--server-process (process-live-p meghanada--server-process))
  530. (set-process-sentinel meghanada--server-process 'ignore)
  531. (set-process-filter meghanada--server-process 'ignore)
  532. (kill-buffer (process-buffer meghanada--server-process))
  533. ;; (kill-process meghanada--server-process)
  534. (delete-process meghanada--server-process)
  535. (setq meghanada--server-process nil)))
  536. (defun meghanada--start-client-process ()
  537. (let (process)
  538. (message "meghanada-client process start ...")
  539. (setq process
  540. (make-network-process
  541. :name "meghanada-client"
  542. :buffer meghanada--client-buffer
  543. :family 'ipv4
  544. :host meghanada--connect-host
  545. :service meghanada--connect-port
  546. :noquery t
  547. :sentinel 'meghanada--client-process-sentinel
  548. :filter 'meghanada--client-process-filter))
  549. (buffer-disable-undo meghanada--client-buffer)
  550. process))
  551. (defun meghanada--start-task-client-process ()
  552. (make-network-process
  553. :name "meghanada-task-client"
  554. :buffer "*meghanada-task-client*"
  555. :family 'ipv4
  556. :host meghanada--connect-host
  557. :service meghanada--connect-port
  558. :noquery t
  559. :sentinel 'meghanada--task-client-process-sentinel
  560. :filter 'meghanada--task-client-process-filter))
  561. (defun meghanada--start-server-and-client ()
  562. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  563. meghanada--client-process
  564. (unless (and meghanada--server-process (process-live-p meghanada--server-process))
  565. (progn
  566. (meghanada--client-kill)
  567. (setq meghanada--server-pending
  568. #'(lambda ()
  569. (setq meghanada--client-process (meghanada--start-client-process))
  570. (message "Please wait. Meghanada indexing ... ")))
  571. (meghanada--get-server-process-create)))))
  572. (defun meghanada--get-client-process-create ()
  573. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  574. meghanada--client-process
  575. (setq meghanada--client-process (meghanada--start-client-process))))
  576. (defun meghanada--client-process-sentinel (process msg)
  577. (unless (process-live-p process)
  578. (set-process-sentinel process 'ignore)
  579. (set-process-filter process 'ignore)
  580. (kill-buffer (process-buffer process))
  581. (delete-process process)
  582. (setq meghanada--client-process nil)
  583. (error (format "Disconnected:meghanada-client process stopped: %s. Please check *meghanada-server-log* buffer" msg))))
  584. (defun meghanada--task-client-process-sentinel (process msg)
  585. (unless (process-live-p process)
  586. (set-process-sentinel process 'ignore)
  587. (set-process-filter process 'ignore)
  588. (kill-buffer (process-buffer process))
  589. (delete-process process)
  590. (setq meghanada--task-client-process nil)
  591. (error (format "Disconnected:meghanada-task-client process stopped: %s. Please check *meghanada-server-log* buffer" msg))))
  592. (defun meghanada--process-client-response (process response)
  593. (let* ((output (read (meghanada--normalize-repsonse-file-path (meghanada--remove-eot response))))
  594. (callback (meghanada--process-pop-callback process))
  595. (status (car output))
  596. (res (car (cdr output))))
  597. (pcase status
  598. (`success
  599. (when callback
  600. (with-demoted-errors "Warning: %S"
  601. (apply (car callback) res (cdr callback)))))
  602. (`error
  603. (ignore-errors
  604. (progn
  605. (message (format "Error:%s . Please check *meghanada-server-log*" res))
  606. (apply (car callback) nil (cdr callback))))))))
  607. (defun meghanada--normalize-repsonse-file-path (response)
  608. (if (eq system-type 'windows-nt)
  609. (let (diver-char downcase-char)
  610. (setq response (replace-regexp-in-string "\\\\" "/" response))
  611. (when (string-match "\\([A-Z]\\):/" response)
  612. (setq diver-char (match-string 1 response))
  613. (setq downcase-char (downcase diver-char))
  614. (setq response (replace-regexp-in-string (concat diver-char ":/") (concat downcase-char ":/") response)))
  615. response))
  616. response)
  617. (defun meghanada--normalize-request-file-path (request)
  618. (if (eq system-type 'windows-nt)
  619. (let (diver-char upcase-char)
  620. (when (string-match "\\([a-z]\\):/" request)
  621. (setq diver-char (match-string 1 request))
  622. (setq upcase-char (upcase diver-char))
  623. (setq request (replace-regexp-in-string (concat diver-char ":/") (concat upcase-char ":/") request)))
  624. (setq request (replace-regexp-in-string "/" "\\\\" request))
  625. request))
  626. request)
  627. (defun meghanada--client-process-filter (process output)
  628. (let ((pbuf (process-buffer process))
  629. responses)
  630. (when meghanada--client-pending
  631. (funcall meghanada--client-pending)
  632. (setq meghanada--client-pending nil))
  633. (when (buffer-live-p pbuf)
  634. (with-current-buffer pbuf
  635. (save-excursion
  636. (goto-char (process-mark process))
  637. (insert output)
  638. (set-marker (process-mark process) (point))
  639. (goto-char (point-min))
  640. (while (search-forward meghanada--eot nil t)
  641. (let ((response (buffer-substring-no-properties (point-min)
  642. (point))))
  643. (delete-region (point-min) (point))
  644. (setq responses (cons (meghanada--remove-eot response) responses))))
  645. (goto-char (process-mark process)))))
  646. ;; Handle all responses.
  647. (mapc #'(lambda (r)
  648. (meghanada--process-client-response process r))
  649. (nreverse responses))))
  650. (defun meghanada--task-client-process-filter (ignored output)
  651. (let ((buf meghanada--task-buffer))
  652. ;; (pop-to-buffer buf)
  653. (with-current-buffer (get-buffer-create buf)
  654. (let ((win (get-buffer-window buf))
  655. (eob (eq (point) (point-max)))
  656. (eot nil))
  657. ;; Insert the new output at the end of the buffer
  658. ;; and restore the buffer point afterward
  659. (save-excursion
  660. (goto-char (point-max))
  661. (setq buffer-read-only nil)
  662. (insert output)
  663. (if (and (string= buf meghanada--junit-buf-name)
  664. (search-backward meghanada--eot nil t))
  665. (progn
  666. (while (re-search-forward meghanada--eot nil t)
  667. (replace-match "")
  668. (setq eot t))
  669. (when eot
  670. (compilation-mode)))
  671. (progn
  672. (while (re-search-backward meghanada--eot nil t)
  673. (replace-match "")
  674. (setq eot t))
  675. (when eot
  676. (compilation-mode))))
  677. (setq buffer-read-only t)
  678. ;; Run all after test hooks now that the buffer is read-only
  679. (when (string= buf meghanada--junit-buf-name)
  680. (run-hooks 'meghanada-mode-after-test-hook)))
  681. ;; If the cursor is already at the end of the buffer or if
  682. ;; auto-scrolling is activated, move the cursor to the end of the buffer
  683. ;; (moves both buffer point and window point)
  684. (when (or meghanada-task-buffer-auto-scroll eob)
  685. (goto-char (point-max))
  686. (when win (set-window-point win (point))))))))
  687. (defun meghanada--process-push-callback (process cb)
  688. (let ((callbacks (process-get process 'meghanada-callback-stack)))
  689. (if callbacks
  690. (nconc callbacks (list cb))
  691. (process-put process 'meghanada-callback-stack (list cb)))))
  692. (defun meghanada--process-pop-callback (process)
  693. (let ((callbacks (process-get process 'meghanada-callback-stack)))
  694. (process-put process 'meghanada-callback-stack (cdr callbacks))
  695. (car callbacks)))
  696. (defun meghanada--client-kill ()
  697. "Disconnect and kill meghanada-client."
  698. (when (and meghanada--client-process (process-live-p meghanada--client-process))
  699. (set-process-sentinel meghanada--client-process 'ignore)
  700. (set-process-filter meghanada--client-process 'ignore)
  701. (kill-buffer (process-buffer meghanada--client-process))
  702. (delete-process meghanada--client-process)
  703. (setq meghanada--client-process nil))
  704. (when (and meghanada--task-client-process (process-live-p meghanada--task-client-process))
  705. (set-process-sentinel meghanada--task-client-process 'ignore)
  706. (set-process-filter meghanada--task-client-process 'ignore)
  707. (kill-buffer (process-buffer meghanada--task-client-process))
  708. (delete-process meghanada--task-client-process)
  709. (setq meghanada--task-client-process nil)))
  710. (defun meghanada--send-request (request callback &rest args)
  711. (let* ((process (meghanada--get-client-process-create))
  712. (argv (cons request args))
  713. (callback (if (listp callback) callback (list callback)))
  714. (send-str (meghanada--normalize-request-file-path (format "%s" argv))))
  715. (when (and process (process-live-p process))
  716. (meghanada--process-push-callback process callback)
  717. (meghanada--without-narrowing
  718. (process-send-string process
  719. (format "%s\n" send-str))))))
  720. (defun meghanada--send-request-process (request process callback &rest args)
  721. (let* ((argv (cons request args))
  722. (callback (if (listp callback) callback (list callback)))
  723. (send-str (format "%s" argv)))
  724. (when (and process (process-live-p process))
  725. (meghanada--process-push-callback process callback)
  726. (meghanada--without-narrowing
  727. (process-send-string process
  728. (format "%s\n" send-str))))))
  729. (defvar meghanada--sync-id 0)
  730. (defvar meghanada--sync-result '(-1 . nil))
  731. (defun meghanada--sync-request-callback (response id)
  732. (setq meghanada--sync-result (cons id response)))
  733. (defun meghanada--send-request-sync (request &rest args)
  734. (let* ((id meghanada--sync-id)
  735. (callback (list #'meghanada--sync-request-callback id)))
  736. (setq meghanada--sync-id (1+ meghanada--sync-id))
  737. (with-local-quit
  738. (let ((process (meghanada--get-client-process-create)))
  739. (apply 'meghanada--send-request request callback args)
  740. (while (not (= id (car meghanada--sync-result)))
  741. (accept-process-output process))
  742. (cdr meghanada--sync-result)))))
  743. ;;
  744. ;; meghanada api
  745. ;;
  746. (defun meghanada-alive-p ()
  747. (and meghanada-mode
  748. meghanada--client-process
  749. (process-live-p meghanada--client-process)))
  750. ;;
  751. ;; import
  752. ;;
  753. (defun meghanada--goto-imports-start ()
  754. (goto-char (point-min))
  755. (let ((package-point (re-search-forward "package .*;" nil t))
  756. (import-point (re-search-forward "import .*;" nil t)))
  757. (cond (import-point (goto-char import-point)
  758. (beginning-of-line))
  759. (package-point (goto-char package-point)
  760. (forward-line)
  761. (open-line 2)
  762. (forward-line))
  763. (t (goto-char (point-min))
  764. (open-line 1)))))
  765. (defun meghanada--import-name (imp)
  766. (let ((cs case-fold-search))
  767. (when cs
  768. (setq case-fold-search nil))
  769. (let ((imp (when (string-match "\\([a-z0-9_]+\\.\\)+[A-Za-z0-9_]+" imp)
  770. (match-string 0 imp))))
  771. (prog1
  772. imp
  773. (when cs
  774. (setq case-fold-search t))))))
  775. (defun meghanada--is-java-lang-package-p (fqcn)
  776. "Check if FQCN belongs to java.lang package (exclude subpackages,
  777. e.g. java.lang.annotation)."
  778. (and
  779. (string-prefix-p "java.lang." fqcn)
  780. (not (string-match-p (regexp-quote "#") fqcn))
  781. (<= (length (split-string fqcn "\\.")) 3)))
  782. (defun meghanada--import-exists-p (imp)
  783. (save-excursion
  784. (goto-char (point-min))
  785. (re-search-forward (concat "^import\\s-+" imp "\\s-*;") nil t)))
  786. (defun meghanada--add-import-callback (result buf)
  787. (with-current-buffer buf
  788. (let ((severity (car result)))
  789. (pcase severity
  790. (`success
  791. (let* ((fqcn (car (cdr result)))
  792. (is-static (string-match-p (regexp-quote "#") fqcn))
  793. (imp (if is-static
  794. (replace-regexp-in-string "#" "." fqcn)
  795. fqcn)))
  796. (unless (or (meghanada--is-java-lang-package-p imp)
  797. (meghanada--import-exists-p imp))
  798. (let ((start t))
  799. (save-excursion
  800. (meghanada--goto-imports-start)
  801. (while (and start (re-search-forward "^import .+;" nil t))
  802. (forward-line)
  803. (setq start (/= (point-at-bol) (point-at-eol))))
  804. (if is-static
  805. (insert (format "import static %s;\n" imp))
  806. (insert (format "import %s;\n" imp))))))))))))
  807. (defun meghanada--add-import (imp buf)
  808. (unless
  809. (or
  810. (meghanada--is-java-lang-package-p imp)
  811. (meghanada--import-exists-p imp))
  812. (meghanada-add-import-async
  813. imp
  814. #'meghanada--add-import-callback
  815. buf)))
  816. (defun meghanada-import-all--callback (result buf optimize)
  817. (with-current-buffer buf
  818. (when result
  819. (mapc
  820. (lambda (imps)
  821. (if (= (length imps) 1)
  822. (meghanada--add-import (car imps) buf)
  823. (let ((res (completing-read "import:" imps nil t)))
  824. (unless (string= res "")
  825. (meghanada--add-import res buf))))) result))
  826. (when optimize
  827. (save-buffer)
  828. (meghanada-optimize-import))))
  829. (defun meghanada-import-at--callback (result buf optimize)
  830. (with-current-buffer buf
  831. (when result
  832. (mapc
  833. (lambda (imports)
  834. (let ((type (car imports))
  835. (imps (cdr imports)))
  836. (pcase type
  837. (`class
  838. (if (= (length imps) 1)
  839. (meghanada--add-import (car imps) buf)
  840. (let ((res (completing-read "import:" imps nil t)))
  841. (unless (string= res "")
  842. (meghanada--add-import res buf)))))
  843. (`method
  844. (if (= (length imps) 1)
  845. (meghanada--add-import (car imps) buf)
  846. (let ((res (completing-read "import:" imps nil t)))
  847. (unless (string= res "")
  848. (meghanada--add-import res buf)))))))) result))
  849. (when optimize
  850. (save-buffer)
  851. (meghanada-optimize-import))))
  852. ;;
  853. ;; meghanada client api
  854. ;;
  855. ;;;###autoload
  856. (defun meghanada-client-direct-connect ()
  857. "Connect the client to a server that is already running."
  858. (interactive)
  859. (meghanada--get-client-process-create))
  860. ;;;###autoload
  861. (defun meghanada-client-connect ()
  862. "Start the server and connect the client"
  863. (interactive)
  864. ;; TODO check
  865. (meghanada--start-server-and-client))
  866. ;;;###autoload
  867. (defun meghanada-client-disconnect ()
  868. "Disconnecting the client."
  869. (interactive)
  870. (meghanada--client-kill))
  871. ;;;###autoload
  872. (defun meghanada-restart ()
  873. "Restart meghanada server and client."
  874. (interactive)
  875. (meghanada--client-kill)
  876. (meghanada-server-kill)
  877. (meghanada--start-server-and-client))
  878. ;;
  879. ;; meghanada other api
  880. ;;
  881. (defun meghanada-ping ()
  882. "Ping the connected server."
  883. (interactive)
  884. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  885. (let ((res (meghanada--send-request-sync "ping")))
  886. (when res
  887. (message (format "%s" res))))
  888. (message "client connection not established")))
  889. (defun meghanada-kill-running-process ()
  890. "Kills another test process running on the server, etc."
  891. (interactive)
  892. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  893. (let ((res (meghanada--send-request-sync "kp")))
  894. (when res
  895. (meghanada--kill-buf meghanada--task-buf-name)
  896. (meghanada--kill-buf meghanada--junit-buf-name)
  897. (message (format "%s" res))))
  898. (message "client connection not established")))
  899. (defun meghanada-clear-cache ()
  900. "Clear the server cache."
  901. (interactive)
  902. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  903. (meghanada--send-request "cc" #'message)
  904. (message "client connection not established")))
  905. ;;
  906. ;; meghanada auto-import api
  907. ;;
  908. (defun meghanada-add-import-async (imp callback buf)
  909. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  910. (meghanada--send-request "ai" (list callback buf) (format "\"%s\"" (buffer-file-name)) imp)
  911. (message "client connection not established")))
  912. (defun meghanada-import-all-async (callback buf optimize)
  913. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  914. (meghanada--send-request "ia" (list callback buf optimize) (format "\"%s\"" (buffer-file-name)))
  915. (message "client connection not established")))
  916. (defun meghanada-import-at-point-async (callback buf optimize)
  917. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  918. (meghanada--send-request "ip"
  919. (list callback buf optimize)
  920. (format "\"%s\"" (buffer-file-name))
  921. (meghanada--what-line)
  922. (meghanada--what-column)
  923. (format "\"%s\"" (meghanada--what-symbol)))
  924. (message "client connection not established")))
  925. (defun meghanada-optimize-import ()
  926. "Optimize the import statement.
  927. If there are unimported classes, we will automatically import them as much as possible. Also, unused import statements are deleted."
  928. (interactive)
  929. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  930. (let ((output (meghanada--send-request-sync
  931. "oi"
  932. (format "\"%s\"" (buffer-file-name))
  933. (meghanada--write-tmpfile))))
  934. (when output
  935. (let ((patchbuf (get-buffer-create "*meghanada-fmt patch*"))
  936. (tmpfile output))
  937. (save-excursion
  938. (widen)
  939. (with-current-buffer patchbuf
  940. (erase-buffer))
  941. (progn
  942. (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile))
  943. (message "Buffer is already formatted")
  944. (meghanada--apply-rcs-patch patchbuf)
  945. (message "Optimized"))))
  946. (kill-buffer patchbuf)
  947. (delete-file tmpfile))))
  948. (message "client connection not established")))
  949. (defun meghanada-import-all ()
  950. "Automatically add all import statements.
  951. If there are unimported classes, we will automatically import them as much as possible."
  952. (interactive)
  953. (meghanada-import-all-async #'meghanada-import-all--callback (current-buffer) nil))
  954. (defun meghanada-import-at-point ()
  955. "Add the import statement for the class on the cursor."
  956. (interactive)
  957. (meghanada-import-at-point-async #'meghanada-import-at--callback (current-buffer) nil))
  958. ;;
  959. ;; meghanada complete api
  960. ;;
  961. (defun meghanada-autocomplete-prefix-async (prefix callback)
  962. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  963. (meghanada--send-request "ap"
  964. callback
  965. (format "\"%s\"" (buffer-file-name))
  966. (meghanada--what-line)
  967. (meghanada--what-column)
  968. (format "\"%s\"" prefix))
  969. (message "client connection not established")))
  970. (defun meghanada-autocomplete-resolve-async (type item desc callback)
  971. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  972. (meghanada--send-request "cr"
  973. callback
  974. (format "\"%s\"" (buffer-file-name))
  975. (meghanada--what-line)
  976. (meghanada--what-column)
  977. (format "\"%s\"" type)
  978. (format "\"%s\"" item)
  979. (format "\"%s\"" desc))
  980. (message "client connection not established")))
  981. (defun meghanada--local-val-callback (result)
  982. (let* ((return-type (car result))
  983. (vals (car (cdr result)))
  984. (len (length vals)))
  985. (when (and (not (string= "void" return-type)) (> len 0))
  986. (back-to-indentation)
  987. (insert (format "%s = " return-type))
  988. (forward-char -3)
  989. (if (= len 1)
  990. (insert (car vals))
  991. (let ((res (completing-read "local variable:" vals nil nil)))
  992. (unless (string= res "")
  993. (insert res)))))))
  994. (defun meghanada-local-variable ()
  995. "Assign a local variable from the return value of an expression."
  996. (interactive)
  997. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  998. (meghanada--send-request "lv"
  999. #'meghanada--local-val-callback
  1000. (format "\"%s\"" (buffer-file-name))
  1001. (meghanada--what-line))
  1002. (message "client connection not established")))
  1003. ;;
  1004. ;; meghanada diagnostics api (flycheck)
  1005. ;;
  1006. (defun meghanada-diagnostics-async (callback)
  1007. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  1008. (meghanada--send-request "di"
  1009. callback
  1010. (format "\"%s\"" (buffer-file-name)))
  1011. (message "client connection not established")))
  1012. (defun meghanada-diagnostic-string-async (callback)
  1013. (let ((buf (buffer-file-name))
  1014. (tmp (meghanada--write-tmpfile)))
  1015. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  1016. (meghanada--send-request "dl"
  1017. callback
  1018. buf
  1019. tmp)
  1020. (message "client connection not established"))))
  1021. ;;
  1022. ;; meghanada interactive-command (async)
  1023. ;;
  1024. ;; (defun meghanada-parse-file ()
  1025. ;; "TODO: FIX DOC ."
  1026. ;; (when (meghanada-alive-p)
  1027. ;; (if (and meghanada--client-process (process-live-p meghanada--client-process))
  1028. ;; (meghanada--send-request "p" #'message (buffer-file-name))
  1029. ;; (message "client connection not established"))))
  1030. ;;
  1031. ;; meghanada compile-command (async)
  1032. ;;
  1033. (defun meghanada--kill-buf (name)
  1034. (when (get-buffer name)
  1035. (delete-windows-on (get-buffer name))
  1036. (kill-buffer name)))
  1037. (defun meghanada--compile-callback (result)
  1038. (let ((severity (car result)))
  1039. (pcase severity
  1040. (`success
  1041. (progn
  1042. (meghanada--kill-buf "*compilation*")
  1043. (message "compile finished")))
  1044. (`error
  1045. (let ((messages (cdr result)))
  1046. (with-current-buffer (get-buffer-create "*compilation*")
  1047. (setq buffer-read-only nil)
  1048. (save-excursion
  1049. (dolist (msg messages)
  1050. (insert (format "%s" msg))
  1051. (open-line 1))
  1052. (goto-char (point-min)))
  1053. (compilation-mode)))))))
  1054. (defun meghanada-compile-file ()
  1055. "Compile the current file."
  1056. (interactive)
  1057. (when (meghanada-alive-p)
  1058. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1059. (let ((buf (format "\"%s\"" (buffer-file-name))))
  1060. (message "compiling ... ")
  1061. (meghanada--kill-buf "*compilation*")
  1062. (pop-to-buffer "*compilation*")
  1063. (meghanada--send-request "c" #'meghanada--compile-callback buf))
  1064. (message "client connection not established"))))
  1065. (defun meghanada-compile-project ()
  1066. "Compile the current project."
  1067. (interactive)
  1068. (when (meghanada-alive-p)
  1069. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1070. (let ((buf (format "\"%s\"" (buffer-file-name))))
  1071. (message "compiling ... ")
  1072. (meghanada--kill-buf "*compilation*")
  1073. (pop-to-buffer "*compilation*")
  1074. (meghanada--send-request "cp" #'meghanada--compile-callback buf))
  1075. (message "client connection not established"))))
  1076. (setq compilation-error-regexp-alist
  1077. (append (list
  1078. ;; works for javac
  1079. '("^\\s-*\\[[^]]*\\]\\s-*\\(.+\\):\\([0-9]+\\):" 1 2)
  1080. ;; works for maven
  1081. '("^\\(\\[ERROR\\] \\)?\\(/[^:]+\\):\\[\\([0-9]+\\),\\([0-9]+\\)\\]" 2 3 4)
  1082. '("^\\sw+(\\(\\sw+\\.\\)+\\(\\sw+\\)).+<<< \\(FAILURE\\|ERROR\\)!$"2))
  1083. compilation-error-regexp-alist))
  1084. ;;
  1085. ;; meghanada junit api
  1086. ;;
  1087. (defun meghanada--switch-testcase-callback (result)
  1088. (when (and result (file-exists-p result))
  1089. (find-file result)))
  1090. (defun meghanada-switch-testcase ()
  1091. "Switch the buffer to the test class of the current class."
  1092. (interactive)
  1093. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1094. (meghanada--send-request "st" #'meghanada--switch-testcase-callback (format "\"%s\"" (buffer-file-name)))
  1095. (message "client connection not established")))
  1096. (defun meghanada--setup-task-buffer (buf-name height)
  1097. (when (not (get-buffer-window buf-name))
  1098. (save-selected-window
  1099. (save-excursion
  1100. (let* ((w (split-window-vertically))
  1101. (h (window-height w)))
  1102. (select-window w)
  1103. (switch-to-buffer buf-name)
  1104. (shrink-window (- h height)))))))
  1105. (defun meghanada--junit-callback (ignored)
  1106. "A junit callback dummy function. IGNORED is not used."
  1107. )
  1108. (defun meghanada--run-junit (file debug test)
  1109. (unless (process-live-p meghanada--task-client-process)
  1110. (setq meghanada--task-client-process (meghanada--start-task-client-process)))
  1111. (if meghanada--task-client-process
  1112. (progn
  1113. (meghanada--kill-buf meghanada--task-buf-name)
  1114. (meghanada--kill-buf meghanada--junit-buf-name)
  1115. (setq meghanada--task-buffer meghanada--junit-buf-name)
  1116. (pop-to-buffer meghanada--junit-buf-name)
  1117. (if debug
  1118. (meghanada--send-request-process "dj" meghanada--task-client-process #'meghanada--junit-callback (format "\"%s\"" file) test)
  1119. (meghanada--send-request-process "rj" meghanada--task-client-process #'meghanada--junit-callback (format "\"%s\"" file) test)))
  1120. (message "client connection not established")))
  1121. (defun meghanada-run-junit-class ()
  1122. "Run the current test class on JUnit."
  1123. (interactive)
  1124. (let* ((file-name (buffer-file-name))
  1125. (test-name (car (split-string
  1126. (car (last (split-string file-name "/")))
  1127. "\\."))))
  1128. (meghanada--run-junit file-name nil test-name)))
  1129. (defun meghanada-run-junit-test-case ()
  1130. "Tests the specified test case for the current test class on JUnit."
  1131. (interactive)
  1132. (let* ((file-name (buffer-file-name))
  1133. (class-name (car (split-string
  1134. (car (last (split-string file-name "/")))
  1135. "\\.")))
  1136. (test-case (completing-read "Test case: "
  1137. (imenu--make-index-alist t)
  1138. nil t
  1139. (which-function)))
  1140. (test-name (format "%s#%s" class-name test-case)))
  1141. (meghanada--run-junit file-name nil test-name)))
  1142. (defun meghanada-debug-junit-class ()
  1143. "Run the current test class in debug mode on JUnit."
  1144. (interactive)
  1145. (let* ((file-name (buffer-file-name))
  1146. (test-name (car (split-string
  1147. (car (last (split-string file-name "/")))
  1148. "\\."))))
  1149. (meghanada--run-junit file-name t test-name)))
  1150. (defun meghanada-debug-junit-test-case ()
  1151. "Tests the specified test case for the current test class in debug mode on JUnit.."
  1152. (interactive)
  1153. (let* ((file-name (buffer-file-name))
  1154. (class-name (car (split-string
  1155. (car (last (split-string file-name "/")))
  1156. "\\.")))
  1157. (test-case (completing-read "Test case: "
  1158. (imenu--make-index-alist t)
  1159. nil t
  1160. (which-function)))
  1161. (test-name (format "%s#%s" class-name test-case)))
  1162. (meghanada--run-junit file-name t test-name)))
  1163. (defun meghanada-run-junit-recent ()
  1164. "Run the most recent test on JUnit."
  1165. (interactive)
  1166. (meghanada--run-junit (buffer-file-name) nil ""))
  1167. (defun meghanada-run-task (args)
  1168. "Runs the tasks of a given maven or gradle project management tool."
  1169. (interactive "sArgs: ")
  1170. (message args)
  1171. (unless (process-live-p meghanada--task-client-process)
  1172. (setq meghanada--task-client-process (meghanada--start-task-client-process)))
  1173. (if (and meghanada--task-client-process (process-live-p meghanada--task-client-process))
  1174. (progn
  1175. (meghanada--kill-buf meghanada--task-buf-name)
  1176. (meghanada--kill-buf meghanada--junit-buf-name)
  1177. (setq meghanada--task-buffer meghanada--task-buf-name)
  1178. (pop-to-buffer meghanada--task-buf-name)
  1179. (meghanada--send-request-process "rt" meghanada--task-client-process #'meghanada--junit-callback args))
  1180. (message "client connection not established")))
  1181. (defun meghanada-exec-main ()
  1182. "Execute the Main class."
  1183. (interactive)
  1184. (unless (process-live-p meghanada--task-client-process)
  1185. (setq meghanada--task-client-process (meghanada--start-task-client-process)))
  1186. (if (and meghanada--task-client-process (process-live-p meghanada--task-client-process))
  1187. (let ((file (format "\"%s\"" (buffer-file-name))))
  1188. (meghanada--kill-buf meghanada--task-buf-name)
  1189. (meghanada--kill-buf meghanada--junit-buf-name)
  1190. (setq meghanada--task-buffer meghanada--task-buf-name)
  1191. (pop-to-buffer meghanada--task-buf-name)
  1192. (meghanada--send-request-process "em" meghanada--task-client-process #'meghanada--junit-callback file))
  1193. (message "client connection not established")))
  1194. (defun meghanada-debug-main ()
  1195. "Execute the Main class in debug mode."
  1196. (interactive)
  1197. (unless (process-live-p meghanada--task-client-process)
  1198. (setq meghanada--task-client-process (meghanada--start-task-client-process)))
  1199. (if (and meghanada--task-client-process (process-live-p meghanada--task-client-process))
  1200. (let ((file (format "\"%s\"" (buffer-file-name))))
  1201. (meghanada--kill-buf meghanada--task-buf-name)
  1202. (meghanada--kill-buf meghanada--junit-buf-name)
  1203. (setq meghanada--task-buffer meghanada--task-buf-name)
  1204. (pop-to-buffer meghanada--task-buf-name)
  1205. (meghanada--send-request-process "dm" meghanada--task-client-process #'meghanada--junit-callback file))
  1206. (message "client connection not established")))
  1207. ;;
  1208. ;; meghanada jump api
  1209. ;;
  1210. (defun meghanada--jump-callback (res)
  1211. (let* ((filename (nth 0 res))
  1212. (line (nth 1 res))
  1213. (col (nth 2 res)))
  1214. (unless (string= filename "")
  1215. (unless (string= filename (buffer-file-name))
  1216. (funcall #'find-file filename))
  1217. (meghanada-mode t)
  1218. (meghanada--goto-line line)
  1219. (beginning-of-line)
  1220. (forward-char (1- col))
  1221. (recenter)
  1222. (if (buffer-modified-p)
  1223. (message "Buffer is modified, file position might not have been correct")))))
  1224. (defun meghanada-jump-declaration ()
  1225. "Jump to the definition of the symbol on the cursor."
  1226. (interactive)
  1227. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1228. (let ((sym (meghanada--what-symbol)))
  1229. (when sym
  1230. (meghanada--send-request "jd" #'meghanada--jump-callback
  1231. (format "\"%s\"" (buffer-file-name))
  1232. (meghanada--what-line)
  1233. (meghanada--what-column)
  1234. (format "\"%s\"" sym))))
  1235. (message "client connection not established")))
  1236. (defun meghanada--list-symbols ()
  1237. "List all available symbols."
  1238. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1239. (let* ((output (meghanada--send-request-sync "ls")))
  1240. (split-string output "\n"))
  1241. (message "client connection not established")))
  1242. (defun meghanada-jump-symbol ()
  1243. "Jump to the specified symbol."
  1244. (interactive)
  1245. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1246. (let ((sym (completing-read "Symbol: " (meghanada--list-symbols) nil nil)))
  1247. (when sym
  1248. (meghanada--send-request "js" #'meghanada--jump-callback
  1249. (format "\"%s\"" (buffer-file-name))
  1250. (meghanada--what-line)
  1251. (meghanada--what-column)
  1252. (format "\"%s\"" sym))))
  1253. (message "client connection not established")))
  1254. (defun meghanada-back-jump ()
  1255. "Jump to the original position before jumping."
  1256. (interactive)
  1257. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1258. (meghanada--send-request "bj" #'meghanada--jump-callback)
  1259. (message "client connection not established")))
  1260. ;;
  1261. ;; meghanada format api
  1262. ;;
  1263. (defun meghanada--write-tmpfile ()
  1264. "Write tmpfile."
  1265. (let ((tmpfile (make-temp-file "meghanada" nil ".java"))
  1266. (coding-system-for-read 'utf-8)
  1267. (coding-system-for-write 'utf-8))
  1268. (unwind-protect
  1269. (write-region nil nil tmpfile nil 'nomsg))
  1270. tmpfile))
  1271. (defun meghanada--apply-rcs-patch (patch-buffer)
  1272. "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer."
  1273. (let ((target-buffer (current-buffer))
  1274. ;; Relative offset between buffer line numbers and line numbers
  1275. ;; in patch.
  1276. ;;
  1277. ;; Line numbers in the patch are based on the source file, so
  1278. ;; we have to keep an offset when making changes to the
  1279. ;; buffer.
  1280. ;;
  1281. ;; Appending lines decrements the offset (possibly making it
  1282. ;; negative), deleting lines increments it. This order
  1283. ;; simplifies the forward-line invocations.
  1284. (line-offset 0))
  1285. (save-excursion
  1286. (with-current-buffer patch-buffer
  1287. (goto-char (point-min))
  1288. (while (not (eobp))
  1289. (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
  1290. (error "Invalid rcs patch or internal error in apply-rcs-patch"))
  1291. (forward-line)
  1292. (let ((action (match-string 1))
  1293. (from (string-to-number (match-string 2)))
  1294. (len (string-to-number (match-string 3))))
  1295. (cond
  1296. ((equal action "a")
  1297. (let ((start (point)))
  1298. (forward-line len)
  1299. (let ((text (buffer-substring start (point))))
  1300. (with-current-buffer target-buffer
  1301. (cl-decf line-offset len)
  1302. (goto-char (point-min))
  1303. (forward-line (- from len line-offset))
  1304. (insert text)))))
  1305. ((equal action "d")
  1306. (with-current-buffer target-buffer
  1307. (meghanada--goto-line (- from line-offset))
  1308. (cl-incf line-offset len)
  1309. (meghanada--delete-whole-line len)))
  1310. (t
  1311. (error "Invalid rcs patch or internal error in apply-rcs-patch")))))))))
  1312. (defun meghanada-code-beautify ()
  1313. "Beautify java code."
  1314. (interactive)
  1315. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1316. (let ((output (meghanada--send-request-sync "fc" (meghanada--write-tmpfile))))
  1317. (when output
  1318. (let ((patchbuf (get-buffer-create "*meghanada-fmt patch*"))
  1319. (tmpfile output))
  1320. (save-excursion
  1321. (widen)
  1322. (with-current-buffer patchbuf
  1323. (erase-buffer))
  1324. (progn
  1325. (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile))
  1326. (message "Buffer is already formatted")
  1327. (meghanada--apply-rcs-patch patchbuf)
  1328. (message "Applied format"))))
  1329. (kill-buffer patchbuf)
  1330. (delete-file tmpfile))))
  1331. (message "client connection not established")))
  1332. (defun meghanada-code-beautify-before-save ()
  1333. "Beautify code before save."
  1334. (when (meghanada-alive-p)
  1335. (meghanada-code-beautify)))
  1336. ;;
  1337. ;; meghanada reference api
  1338. ;;
  1339. (defun meghanada--reference-callback (messages)
  1340. "Show reference result."
  1341. (if messages
  1342. (with-current-buffer (get-buffer-create meghanada--ref-buf-name)
  1343. (setq buffer-read-only nil)
  1344. (save-excursion
  1345. (dolist (msg messages)
  1346. (insert (format "%s\n" (nth 0 msg)))))
  1347. (compilation-mode))
  1348. (progn
  1349. (meghanada--kill-buf meghanada--ref-buf-name)
  1350. (message "no reference found"))))
  1351. (defun meghanada--reference-prepare ()
  1352. (meghanada--kill-buf meghanada--ref-buf-name)
  1353. (pop-to-buffer meghanada--ref-buf-name)
  1354. (message "searching ..."))
  1355. (defun meghanada-reference ()
  1356. "Searches for a reference to a field or method on the cursor."
  1357. (interactive)
  1358. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1359. (let ((sym (meghanada--what-symbol))
  1360. (buf (buffer-file-name))
  1361. (line (meghanada--what-line))
  1362. (col (meghanada--what-column)))
  1363. (when sym
  1364. (progn
  1365. (funcall meghanada-reference-prepare)
  1366. (meghanada--send-request "re" meghanada-reference-callback
  1367. (format "\"%s\"" buf)
  1368. line
  1369. col
  1370. (format "\"%s\"" sym)))))
  1371. (message "client connection not established")))
  1372. ;;
  1373. ;; meghanada type-info api
  1374. ;;
  1375. (defun meghanada--typeinfo-callback (messages)
  1376. (let ((fqcn (nth 0 messages))
  1377. (classes (nth 1 messages))
  1378. (interfaces (nth 2 messages))
  1379. (members (nth 3 messages))
  1380. (indent 0))
  1381. (if (and fqcn (not (string-empty-p fqcn)))
  1382. (with-help-window (get-buffer-create meghanada--typeinfo-buf-name)
  1383. (save-excursion
  1384. (insert (propertize (format "Class: ")
  1385. 'face '(:weight bold)))
  1386. (insert (format "%s\n\n" fqcn))
  1387. (dolist (c classes)
  1388. (dotimes (number indent 0)
  1389. (insert " "))
  1390. (insert (format "%s\n" c))
  1391. (setq indent (+ indent 2)))
  1392. (when (> (length interfaces) 0)
  1393. (insert "\n")
  1394. (insert (propertize (format "Implements:\n")
  1395. 'face '(:weight bold)))
  1396. (dolist (it interfaces)
  1397. (insert (format " %s\n" it))))
  1398. (when (> (length members) 0)
  1399. (insert "\n")
  1400. (insert (propertize (format "Members:\n")
  1401. 'face '(:weight bold)))
  1402. (dolist (m members)
  1403. (insert (format " %s\n" (nth 1 m)))))
  1404. (setq buffer-read-only t)))
  1405. (progn
  1406. (meghanada--kill-buf meghanada--typeinfo-buf-name)
  1407. (message "no type found")))))
  1408. (defun meghanada--typeinfo-prepare ()
  1409. (meghanada--kill-buf meghanada--typeinfo-buf-name)
  1410. (pop-to-buffer meghanada--typeinfo-buf-name)
  1411. (message "searching ..."))
  1412. (defun meghanada-typeinfo ()
  1413. "Show information about the class on the cursor."
  1414. (interactive)
  1415. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1416. (let ((sym (meghanada--what-symbol))
  1417. (buf (buffer-file-name))
  1418. (line (meghanada--what-line))
  1419. (col (meghanada--what-column)))
  1420. (progn
  1421. (funcall meghanada-typeinfo-prepare)
  1422. (meghanada--send-request "ti" meghanada-typeinfo-callback
  1423. (format "\"%s\"" buf)
  1424. line
  1425. col
  1426. (format "\"%s\"" sym))))
  1427. (message "client connection not established")))
  1428. ;;
  1429. ;; meghanada search everywhere api
  1430. ;;
  1431. (defun meghanada--search-callback (messages)
  1432. "Show search everywhere result."
  1433. (if messages
  1434. (with-current-buffer (get-buffer-create meghanada--search-buf-name)
  1435. (setq buffer-read-only nil)
  1436. (let* ((classes (nth 0 messages))
  1437. (methods (nth 1 messages))
  1438. (symbols (nth 2 messages))
  1439. (usages (nth 3 messages))
  1440. (contents (nth 4 messages)))
  1441. (save-excursion
  1442. (when (> (length classes) 0)
  1443. (insert (propertize (format "Classes: ")
  1444. 'face '(:weight bold)))
  1445. (insert "\n")
  1446. (dolist (c classes)
  1447. (insert (format "%s\n" c)))
  1448. (insert "\n"))
  1449. (when (> (length methods) 0)
  1450. (insert (propertize (format "Methods: ")
  1451. 'face '(:weight bold)))
  1452. (insert "\n")
  1453. (dolist (c methods)
  1454. (insert (format "%s\n" c)))
  1455. (insert "\n"))
  1456. (when (> (length symbols) 0)
  1457. (insert (propertize (format "Symbols: ")
  1458. 'face '(:weight bold)))
  1459. (insert "\n")
  1460. (dolist (c symbols)
  1461. (insert (format "%s\n" c)))
  1462. (insert "\n"))
  1463. (when (> (length usages) 0)
  1464. (insert (propertize (format "Usages: ")
  1465. 'face '(:weight bold)))
  1466. (insert "\n")
  1467. (dolist (c usages)
  1468. (insert (format "%s\n" c)))
  1469. (insert "\n"))
  1470. (when (> (length contents) 0)
  1471. (insert (propertize (format "Code:\n")
  1472. 'face '(:weight bold)))
  1473. (dolist (c contents)
  1474. (insert (format "%s\n" c))))
  1475. (compilation-mode))))
  1476. (progn
  1477. (meghanada--kill-buf meghanada--search-buf-name)
  1478. (message "not found"))))
  1479. (defun meghanada--search-prepare ()
  1480. (meghanada--kill-buf meghanada--search-buf-name)
  1481. (pop-to-buffer meghanada--search-buf-name))
  1482. (defun meghanada--call-search-everywhere (query)
  1483. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1484. (progn
  1485. (funcall meghanada-search-prepare)
  1486. (meghanada--send-request
  1487. "se"
  1488. meghanada-search-callback
  1489. query))
  1490. (message "client connection not established")))
  1491. (defvar meghanada--search-everywhere-last nil)
  1492. (defun meghanada--check-searcheverywhere-input ()
  1493. (when (string-prefix-p "*Minibuf" (string-trim (buffer-name)))
  1494. (let* ((content (minibuffer-contents))
  1495. (len (length content)))
  1496. (when (and
  1497. (> len 1)
  1498. (not (string= content meghanada--search-everywhere-last)))
  1499. (setq meghanada--search-everywhere-last content)
  1500. (meghanada--call-search-everywhere content)))))
  1501. (defun meghanada--read-search ()
  1502. (let* (timer is-input)
  1503. (unwind-protect
  1504. (minibuffer-with-setup-hook
  1505. #'(lambda ()
  1506. (setq timer (run-with-idle-timer
  1507. 0.5
  1508. 'repeat
  1509. (lambda ()
  1510. (meghanada--check-searcheverywhere-input)))))
  1511. (prog1 (read-from-minibuffer "Search: ")
  1512. (setq is-input t)))
  1513. (when timer
  1514. (cancel-timer timer)
  1515. (setq timer nil)))))
  1516. (defun meghanada--make-search-query (q)
  1517. (format "\"class:*%s* OR method:*%s* OR symbol:%s\"" q q q))
  1518. (defun meghanada-search-everywhere ()
  1519. "Search everywhere."
  1520. (interactive)
  1521. (let* ((input (read-from-minibuffer "Search: "))
  1522. (query (funcall meghanada-make-search-query input)))
  1523. (meghanada--call-search-everywhere query)))
  1524. (defun meghanada-search-everywhere-ex ()
  1525. "Search everywhere expert mode."
  1526. (interactive)
  1527. (let* ((input (read-from-minibuffer "Lucene query: "))
  1528. (query (format "\"%s\"" input)))
  1529. (meghanada--call-search-everywhere query)))
  1530. ;;
  1531. ;; meghanada show project api
  1532. ;;
  1533. (defun meghanada--show-project-callback (msg)
  1534. (with-help-window (get-buffer-create meghanada--show-project-buf-name)
  1535. (pop-to-buffer meghanada--show-project-buf-name)
  1536. (save-excursion
  1537. (setq buffer-read-only nil)
  1538. (insert (format "%s\n" msg))
  1539. (compilation-mode)
  1540. (setq buffer-read-only t))))
  1541. (defun meghanada--show-project-prepare ()
  1542. (meghanada--kill-buf meghanada--show-project-buf-name))
  1543. (defun meghanada-show-project ()
  1544. "Show more information about the current project."
  1545. (interactive)
  1546. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1547. (progn
  1548. (meghanada--show-project-prepare)
  1549. (meghanada--send-request
  1550. "sp"
  1551. #'meghanada--show-project-callback))
  1552. (message "client connection not established")))
  1553. ;;
  1554. ;; meghanada-mode
  1555. ;;
  1556. (defvar meghanada-mode-map
  1557. (let* ((map (make-sparse-keymap))
  1558. (prefix-map (make-sparse-keymap)))
  1559. (define-key prefix-map (kbd "C-c c") 'meghanada-compile-project)
  1560. (define-key prefix-map (kbd "C-c C-c") 'meghanada-compile-file)
  1561. (define-key prefix-map (kbd "C-r o") 'meghanada-optimize-import)
  1562. (define-key prefix-map (kbd "C-r i") 'meghanada-import-all)
  1563. (define-key prefix-map (kbd "C-r r") 'meghanada-local-variable)
  1564. (define-key prefix-map (kbd "C-c t") 'meghanada-run-junit-test-case)
  1565. (define-key prefix-map (kbd "C-c C-t") 'meghanada-run-junit-class)
  1566. (define-key prefix-map (kbd "C-v t") 'meghanada-run-task)
  1567. (define-key map (kbd "C-M-,") 'meghanada-switch-testcase)
  1568. (define-key map meghanada-mode-key-prefix prefix-map)
  1569. map)
  1570. "Keymap for Meghanada-mode.")
  1571. (easy-menu-define meghanada-mode-menu meghanada-mode-map
  1572. "Menu for Meghanada mode"
  1573. '("Meghanada"
  1574. ("Test"
  1575. ["Run junit class" meghanada-run-junit-class]
  1576. ["Run junit testcase" meghanada-run-junit-test-case])
  1577. ("Navigation"
  1578. ["Goto declaration" meghanada-jump-declaration]
  1579. ["Go back" meghanada-back-jump]
  1580. ["Goto testcase" meghanada-switch-testcase])
  1581. ("Project"
  1582. ["Run task" meghanada-run-task])
  1583. ("Compile"
  1584. ["Compile file" meghanada-compile-file]
  1585. ["Compile project" meghanada-compile-project])
  1586. ("Refactor"
  1587. ["Format code" meghanada-code-beautify]
  1588. ["Optimize import" meghanada-optimize-import]
  1589. ["Import all" meghanada-import-all]
  1590. ["Introduce local variable" meghanada-local-variable])))
  1591. (defun meghanada-change-project ()
  1592. "Change project root."
  1593. (when (meghanada-alive-p)
  1594. (if (and meghanada--client-process (process-live-p meghanada--client-process))
  1595. (meghanada--send-request "pc" #'identity (format "\"%s\"" (buffer-file-name)))
  1596. (message "client connection not established"))))
  1597. ;;;###autoload
  1598. (define-minor-mode meghanada-mode
  1599. "A better java development mode for Emacs (minor-mode).
  1600. \\{meghanada-mode-map}"
  1601. nil
  1602. nil
  1603. meghanada-mode-map
  1604. (progn
  1605. (meghanada-install-server)
  1606. (when meghanada-use-company
  1607. (meghanada-company-enable))
  1608. (when meghanada-use-flycheck
  1609. (meghanada-flycheck-enable))
  1610. (when meghanada-use-eldoc
  1611. (meghanada-eldoc-enable))
  1612. (when meghanada-auto-start
  1613. (meghanada-client-connect))
  1614. (meghanada-change-project)
  1615. (add-hook 'xref-backend-functions 'meghanada-xref-backend nil t)
  1616. (run-at-time 2 nil
  1617. #'(lambda ()
  1618. (when (member meghanada--install-err-buf-name
  1619. (mapcar #'(lambda (b)
  1620. (format "%s" b))
  1621. (buffer-list)))
  1622. (pop-to-buffer meghanada--install-err-buf-name))))))
  1623. (remove-hook 'java-mode-hook 'wisent-java-default-setup)
  1624. (add-to-list 'minor-mode-alist
  1625. '(meghanada-mode (:eval (meghanada-modeline-string))))
  1626. (defun meghanada-modeline-string ()
  1627. "Return modeline string."
  1628. (cond ((not (meghanada-alive-p))
  1629. " MEGHANADA:Disconnected")
  1630. ((meghanada-alive-p)
  1631. " MEGHANADA")))
  1632. (defun meghanada-xref-backend ()
  1633. "Meghanada xref backend."
  1634. 'meghanada)
  1635. (cl-defmethod xref-backend-identifier-at-point ((_backend (eql meghanada)))
  1636. (propertize (or (thing-at-point 'symbol) "")
  1637. 'identifier-at-point t))
  1638. ;; (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql meghanada)))
  1639. ;; (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1640. ;; (let* ((sym (meghanada--what-symbol))
  1641. ;; (buf (buffer-file-name))
  1642. ;; (line (meghanada--what-line))
  1643. ;; (col (meghanada--what-column))
  1644. ;; (output (meghanada--send-request-sync "ti" (format "\"%s\"" buf) line col (format "\"%s\"" sym))))
  1645. ;; (let ((members (nth 3 output)))
  1646. ;; (mapcar #'(lambda(m) (nth 0 m)) members)))
  1647. ;; (message "client connection not established")))
  1648. (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql meghanada))))
  1649. (cl-defmethod xref-backend-definitions ((_backend (eql meghanada)) symbol)
  1650. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1651. (when symbol
  1652. (let ((output (meghanada--send-request-sync "jd"
  1653. (format "\"%s\"" (buffer-file-name))
  1654. (meghanada--what-line)
  1655. (meghanada--what-column)
  1656. (format "\"%s\"" symbol))))
  1657. (when output
  1658. (let ((file (nth 0 output))
  1659. (line (nth 1 output))
  1660. (column (nth 2 output)))
  1661. (list (xref-make file (xref-make-file-location file line column))))))
  1662. )
  1663. (message "client connection not established")))
  1664. (cl-defmethod xref-backend-references ((_backend (eql meghanada)) symbol)
  1665. (if (and meghanada--server-process (process-live-p meghanada--server-process))
  1666. (let ((buf (buffer-file-name))
  1667. (line (meghanada--what-line))
  1668. (col (meghanada--what-column)))
  1669. (when symbol
  1670. (let ((output (meghanada--send-request-sync "re" (format "\"%s\"" buf) line col (format "\"%s\"" symbol))))
  1671. (mapcar #'(lambda (m)
  1672. (let* ((info (nth 1 m))
  1673. (desc (nth 0 info))
  1674. (file (nth 1 info))
  1675. (line (nth 2 info))
  1676. (column (nth 3 info)))
  1677. (xref-make desc (xref-make-file-location file line column)))) output))))
  1678. (message "client connection not established")))
  1679. ;; (cl-defmethod xref-backend-apropos ((_backend (eql meghanada)) pattern)
  1680. ;; (message (format "xref-backend-apropos %s" pattern)))
  1681. (provide 'meghanada)
  1682. ;;; meghanada.el ends here