2021-02-21 02:59:11 +00:00
|
|
|
;; -*- lexical-binding: t; -*-
|
|
|
|
|
2021-02-20 19:57:40 +00:00
|
|
|
;; IDE features
|
|
|
|
|
2023-04-21 02:30:29 +00:00
|
|
|
;; Corfu-mode provides inline autocompletion
|
|
|
|
(use-package corfu
|
2023-04-27 18:59:24 +00:00
|
|
|
:straight (:files (:defaults "extensions/corfu-echo.el"))
|
2023-04-21 02:30:29 +00:00
|
|
|
:init
|
2023-04-21 03:23:50 +00:00
|
|
|
(global-corfu-mode)
|
2023-04-27 18:59:24 +00:00
|
|
|
(corfu-echo-mode)
|
2023-04-21 03:23:50 +00:00
|
|
|
:config
|
|
|
|
(defun corfu-move-to-minibuffer ()
|
|
|
|
(interactive)
|
|
|
|
(when completion-in-region--data
|
|
|
|
(let ((completion-extra-properties corfu--extra)
|
|
|
|
completion-cycle-threshold completion-cycling)
|
|
|
|
(apply #'consult-completion-in-region completion-in-region--data))))
|
|
|
|
(defun corfu-enable-in-minibuffer ()
|
|
|
|
"Enable Corfu in the minibuffer if `completion-at-point' is bound."
|
|
|
|
(when (where-is-internal #'completion-at-point (list (current-local-map)))
|
|
|
|
(setq-local corfu-echo-delay nil ;; Disable automatic echo and popup
|
|
|
|
corfu-popupinfo-delay nil)
|
|
|
|
(corfu-mode 1)))
|
|
|
|
(add-hook 'minibuffer-setup-hook #'corfu-enable-in-minibuffer)
|
|
|
|
:general
|
|
|
|
(corfu-map "S-SPC" #'corfu-insert-separator
|
2023-04-27 18:59:24 +00:00
|
|
|
"M-m" #'corfu-move-to-minibuffer)
|
|
|
|
:custom-face
|
|
|
|
(corfu-echo ((t :inherit default))))
|
2021-02-20 19:57:40 +00:00
|
|
|
|
|
|
|
;; Quick file overview for supported modes
|
|
|
|
(use-package imenu
|
|
|
|
:straight (:type built-in)
|
|
|
|
:general
|
|
|
|
(leader-map "m" #'imenu))
|
|
|
|
|
|
|
|
;; Find definition/references
|
|
|
|
(use-package xref
|
|
|
|
:straight (:type built-in)
|
|
|
|
:general
|
|
|
|
(normal "M-," #'xref-pop-marker-stack)
|
2021-02-20 22:49:36 +00:00
|
|
|
(normal "M-r" #'xref-find-references)
|
|
|
|
:custom
|
|
|
|
(xref-prompt-for-identifier nil))
|
|
|
|
|
2023-04-05 01:33:56 +00:00
|
|
|
(use-package eglot
|
|
|
|
:commands (eglot)
|
2023-04-05 12:04:43 +00:00
|
|
|
:config
|
2023-09-26 14:38:05 +00:00
|
|
|
(defun my-eglot-managed-hook ()
|
|
|
|
(if (eglot-managed-p)
|
|
|
|
(setq-local corfu-preview-current nil)
|
|
|
|
(kill-local-variable 'corfu-preview-current)))
|
|
|
|
(add-hook 'eglot-managed-mode-hook #'my-eglot-managed-hook)
|
2023-04-05 12:04:43 +00:00
|
|
|
(add-to-list 'eglot-server-programs
|
|
|
|
'(js-web-mode . ("typescript-language-server" "--stdio")))
|
|
|
|
(add-to-list 'eglot-server-programs
|
|
|
|
`(html-web-mode . ,(eglot-alternatives
|
|
|
|
'(("vscode-html-language-server" "--stdio")
|
|
|
|
("html-languageserver" "--stdio")))))
|
2023-05-01 14:13:43 +00:00
|
|
|
(defvar eglot-java-java-agent nil
|
|
|
|
"Java agent JVM arg for eglot JDTLS.")
|
2023-04-18 13:35:30 +00:00
|
|
|
;; Custom eglot java server for deeper customization
|
|
|
|
(defclass eglot-java-server (eglot-lsp-server) ()
|
|
|
|
:documentation "Eglot integration with JDTLS.")
|
2023-05-01 14:13:43 +00:00
|
|
|
(defun eglot-java-contact (&optional interactive)
|
|
|
|
`(eglot-java-server .
|
|
|
|
("jdtls" ,(if (s-blank? eglot-java-java-agent)
|
|
|
|
""
|
|
|
|
(format "--jvm-arg=-javaagent:%s" eglot-java-java-agent))
|
|
|
|
:initializationOptions
|
|
|
|
(:extendedClientCapabilities (:classFileContentsSupport t)
|
|
|
|
:settings
|
|
|
|
(:java
|
|
|
|
(:home "/Library/Java/JavaVirtualMachines/amazon-corretto-17.jdk/Contents/Home"
|
|
|
|
:configuration
|
|
|
|
(:runtimes [(:name "JavaSE-11"
|
|
|
|
:path "/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk")
|
|
|
|
(:name "JavaSE-17"
|
|
|
|
:path "/Library/Java/JavaVirtualMachines/amazon-corretto-17.jdk"
|
|
|
|
:default t)])))))))
|
|
|
|
(add-to-list 'eglot-server-programs '(java-mode . eglot-java-contact))
|
2023-04-18 13:35:30 +00:00
|
|
|
;; Fix JDTLS's weird handling of workspaceEdit
|
|
|
|
(cl-defmethod eglot-execute-command
|
|
|
|
((_server eglot-java-server) (_cmd (eql java.apply.workspaceEdit)) arguments)
|
|
|
|
"Eclipse JDT breaks spec and replies with edits as arguments."
|
|
|
|
(mapc #'eglot--apply-workspace-edit arguments))
|
|
|
|
;; Support jdtls' ability to jump into class files
|
2023-05-01 17:20:53 +00:00
|
|
|
(define-advice file-relative-name
|
|
|
|
(:before-until (filename &optional directory) jdt-uri)
|
|
|
|
(when (string-match-p "\\`jdt://" filename)
|
|
|
|
filename))
|
|
|
|
(define-hash-table-test 'jdt-file-to-uri-hash 'equal 'sxhash-equal)
|
|
|
|
(defvar eglot-java--jdt-file-to-uri (make-hash-table :test 'jdt-file-to-uri-hash)
|
|
|
|
"Internal variable to map temporary filepaths to JDT URIs.")
|
|
|
|
(define-advice eglot--path-to-uri
|
|
|
|
(:before-until (path) jdt-path)
|
|
|
|
(gethash (file-truename path) eglot-java--jdt-file-to-uri))
|
2023-04-06 16:51:47 +00:00
|
|
|
(defun jdt-class-file-name-handler (operation &rest args)
|
|
|
|
"File name handler for jdtls' `jdt://' URIs."
|
|
|
|
(cond
|
|
|
|
((eq operation 'temporary-file-directory) temporary-file-directory)
|
|
|
|
((member operation '(file-remote-p
|
|
|
|
file-name-case-insensitive-p
|
|
|
|
make-auto-save-file-name
|
|
|
|
find-file-backup-name)) nil)
|
|
|
|
(t (let* ((uri (car args))
|
|
|
|
(uri-hash (secure-hash 'md5 uri))
|
|
|
|
(filename (save-match-data
|
2023-05-01 17:20:53 +00:00
|
|
|
(string-match "jdt://contents/\\(.*?\\)/\\(.*?\\)\.class\\?" uri)
|
2023-04-06 16:51:47 +00:00
|
|
|
(format "%s.java"
|
|
|
|
(replace-regexp-in-string "/" "." (match-string 2 uri) t t))))
|
|
|
|
(temp-dir (expand-file-name "eglot-jdtls" temporary-file-directory))
|
|
|
|
(uri-temp-dir (expand-file-name uri-hash temp-dir))
|
|
|
|
(filepath (concat (file-name-as-directory uri-temp-dir) filename))
|
|
|
|
(metadata-path (format "%s.%s.metadata"
|
|
|
|
(file-name-directory filepath)
|
|
|
|
(file-name-base filepath))))
|
2023-05-01 17:20:53 +00:00
|
|
|
(unless (gethash (file-truename filepath) eglot-java--jdt-file-to-uri)
|
|
|
|
(puthash (file-truename filepath) uri eglot-java--jdt-file-to-uri))
|
2023-04-06 16:51:47 +00:00
|
|
|
(unless (or (file-readable-p filepath) (not (eglot-current-server)))
|
|
|
|
(let ((contents
|
|
|
|
(jsonrpc-request (eglot-current-server)
|
|
|
|
:java/classFileContents
|
|
|
|
(list :uri uri))))
|
|
|
|
(unless (file-directory-p uri-temp-dir) (make-directory uri-temp-dir t))
|
|
|
|
(with-temp-file filepath (insert contents))
|
|
|
|
(with-temp-file metadata-path (insert uri))))
|
|
|
|
(cond
|
|
|
|
((eq operation 'get-file-buffer) (get-buffer filename))
|
2023-05-01 17:20:53 +00:00
|
|
|
((member operation '(expand-file-name
|
|
|
|
directory-file-name
|
|
|
|
file-truename
|
|
|
|
file-name-directory
|
|
|
|
file-name-nondirectory)) (apply operation filepath (cdr args)))
|
2023-04-06 16:51:47 +00:00
|
|
|
((eq operation 'insert-file-contents)
|
|
|
|
(seq-let (uri visit beg end replace) args
|
|
|
|
(let ((content (with-temp-buffer
|
|
|
|
(insert-file-contents filepath nil beg end replace)
|
|
|
|
(buffer-substring (point-min) (point-max)))))
|
|
|
|
(insert content)
|
|
|
|
(when visit
|
2023-05-01 17:20:53 +00:00
|
|
|
(set-visited-file-name filepath t)
|
2023-04-06 16:51:47 +00:00
|
|
|
(set-buffer-modified-p nil)
|
|
|
|
(read-only-mode))
|
|
|
|
(list uri (length content)))))
|
|
|
|
(t (let ((inhibit-file-name-handlers
|
|
|
|
(cons 'jdt-class-file-name-handler
|
|
|
|
(and (eq inhibit-file-name-operation operation)
|
|
|
|
inhibit-file-name-handlers)))
|
|
|
|
(inhibit-file-name-operation operation))
|
|
|
|
(apply operation args))))))))
|
|
|
|
(setq eglot-extend-to-xref t)
|
|
|
|
(add-to-list 'file-name-handler-alist '("\\`jdt://" . jdt-class-file-name-handler))
|
|
|
|
(add-to-list 'auto-mode-alist '("\\`jdt://" . java-mode))
|
2023-04-17 16:49:33 +00:00
|
|
|
;; Java build command
|
|
|
|
(defun eglot-java-maven-build ()
|
|
|
|
(interactive)
|
|
|
|
(if-let ((pom-path (locate-dominating-file default-directory "pom.xml")))
|
|
|
|
(let ((pom-uri (eglot--path-to-uri pom-path)))
|
|
|
|
(jsonrpc-notify
|
|
|
|
(eglot--current-server-or-lose)
|
|
|
|
:java/projectConfigurationUpdate
|
|
|
|
(list :uri pom-uri))
|
|
|
|
(jsonrpc-notify
|
|
|
|
(eglot--current-server-or-lose)
|
|
|
|
:java/buildWorkspace
|
|
|
|
'((:json-false))))
|
|
|
|
(user-error "This doesn't appear to be a Maven project, could not find pom.xml.")))
|
2023-04-05 15:17:13 +00:00
|
|
|
:init
|
|
|
|
(defvar eglot-prefix-map (make-sparse-keymap)
|
|
|
|
"Prefix keymap for eglot commands.")
|
|
|
|
(general-def eglot-prefix-map
|
|
|
|
"a" #'eglot-code-actions
|
|
|
|
"r" #'eglot-reconnect
|
|
|
|
"s" #'eglot-shutdown
|
|
|
|
"R" #'eglot-rename
|
|
|
|
"f" #'eglot-format-buffer
|
|
|
|
"i" #'eglot-code-action-organize-imports
|
|
|
|
"h" #'eldoc
|
|
|
|
"e" #'flymake-show-buffer-diagnostics
|
|
|
|
"E" #'flymake-show-project-diagnostics)
|
|
|
|
:general
|
|
|
|
(eglot-mode-map "C-c l" `(,eglot-prefix-map :which-key "eglot"))
|
2021-02-20 22:49:36 +00:00
|
|
|
:hook
|
2023-04-05 01:33:56 +00:00
|
|
|
(java-mode . eglot-ensure)
|
|
|
|
(rust-mode . eglot-ensure)
|
|
|
|
(js-mode . eglot-ensure)
|
|
|
|
(typescript-mode . eglot-ensure)
|
2023-04-05 12:04:43 +00:00
|
|
|
(python-mode . eglot-ensure)
|
|
|
|
(js-web-mode . eglot-ensure)
|
2023-04-07 17:29:15 +00:00
|
|
|
(html-web-mode . eglot-ensure)
|
2023-04-17 16:49:46 +00:00
|
|
|
(scala-mode . eglot-ensure)
|
2023-05-05 19:41:28 +00:00
|
|
|
(c-mode . eglot-ensure)
|
2024-02-11 05:12:04 +00:00
|
|
|
(terraform-mode . eglot-ensure)
|
2024-02-11 05:11:50 +00:00
|
|
|
(ruby-mode . eglot-ensure)
|
2023-05-05 19:41:28 +00:00
|
|
|
:custom
|
|
|
|
(eglot-confirm-server-initiated-edits nil))
|
2022-04-20 19:53:36 +00:00
|
|
|
|
2021-02-20 23:23:42 +00:00
|
|
|
;; Some compilation-mode conveniences
|
|
|
|
(use-package compile
|
|
|
|
:straight (:type built-in)
|
|
|
|
:commands compile
|
|
|
|
:config
|
2021-02-26 21:46:57 +00:00
|
|
|
(defun postprocess-compilation-buffer ()
|
|
|
|
(goto-char compilation-filter-start)
|
|
|
|
(when (looking-at "\033c")
|
|
|
|
(delete-region (point-min) (match-end 0)))
|
|
|
|
(ansi-color-apply-on-region (point) (point-max)))
|
|
|
|
(add-hook 'compilation-filter-hook 'postprocess-compilation-buffer))
|
|
|
|
|
2021-02-20 23:24:06 +00:00
|
|
|
;; Code formatting library
|
|
|
|
(use-package apheleia
|
|
|
|
:straight (apheleia :host github :repo "raxod502/apheleia")
|
|
|
|
:general
|
|
|
|
("C-c f" #'apheleia-format-buffer))
|
|
|
|
|
2021-03-11 18:49:48 +00:00
|
|
|
;; Debugger interface
|
|
|
|
(use-package realgud
|
2023-11-06 18:33:16 +00:00
|
|
|
:defer t)
|
|
|
|
|
|
|
|
(use-package realgud-jdb
|
|
|
|
:commands realgud:jdb-maven
|
2021-03-11 18:49:48 +00:00
|
|
|
:config
|
2023-11-06 18:33:16 +00:00
|
|
|
(defun realgud:jdb-maven ()
|
|
|
|
"Runs realgud:jdb, setting the classpath from Maven."
|
|
|
|
(interactive)
|
|
|
|
(let ((default-directory (project-root (project-current))))
|
|
|
|
(when (not (locate-dominating-file default-directory "pom.xml"))
|
|
|
|
(user-error "Not a Maven project."))
|
|
|
|
(with-env `(("CLASSPATH" .
|
|
|
|
,(s-trim
|
|
|
|
(shell-command-to-string
|
|
|
|
"mvn -q exec:exec -Dexec.executable=echo -Dexec.args=\"%classpath\""))))
|
|
|
|
(call-interactively #'realgud--jdb)))))
|
2021-03-11 18:49:48 +00:00
|
|
|
|
2021-02-20 19:57:40 +00:00
|
|
|
(provide 'init-ide)
|