dotfiles/emacs/.emacs.d/config/init-ide.el

237 lines
9.8 KiB
EmacsLisp
Raw Normal View History

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
: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)
(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
"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)
: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)
(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")))))
(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.")
(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
(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))
(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
(string-match "jdt://contents/\\(.*?\\)/\\(.*?\\)\.class\\?" uri)
(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))))
(unless (gethash (file-truename filepath) eglot-java--jdt-file-to-uri)
(puthash (file-truename filepath) uri eglot-java--jdt-file-to-uri))
(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))
((member operation '(expand-file-name
directory-file-name
file-truename
file-name-directory
file-name-nondirectory)) (apply operation filepath (cdr args)))
((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
(set-visited-file-name filepath t)
(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))
;; 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.")))
: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)
(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)
(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)
: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
(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)