diff --git a/README.org b/README.org index adf0110..1f1219c 100644 --- a/README.org +++ b/README.org @@ -5,41 +5,72 @@ suggestions to help us improve it! #+end_quote -~jal~ is an Emacs package designed to simplify the injection of Java agents -(like Lombok, JaCoCo, etc.) into the JDT Language Server (JDTLS) process used by -~lsp-java~ and ~eglot-java~. +In the Java ecosystem, a Java Agent is a native JVM plugin that uses bytecode +instrumentation to modify compiled code on the fly. To use one, you must +explicitly pass its JAR file path via the ~-javaagent:/path/to/agent.jar~ +argument when launching the JVM. -It automatically detects configured agents in your project's dependencies (Maven -or Gradle), resolves their JAR paths, and appends the appropriate ~-javaagent~ -arguments to your LSP server's startup command. +JAL is an Emacs package that automates this manual configuration for your +development environment. It bridges the gap between your project’s build +configuration and your LSP client (=lsp-java= or =eglot-java=) by managing that +command-line injection behind the scenes. + +#+begin_quote +*Historical Note*: In the Java ecosystem, "Java Agents" have existed as a core +JVM bytecode technology since 2004 (Java 5)--long before the rise of modern AI. +Despite the name, this package is strictly a utility for JVM bytecode +instrumentation management and has absolutely no relation to AI assistants, LLM +workflows, or autonomous software agents. +#+end_quote + +** How It Works + +- /Automatic Detection/: When you open a Java project, JAL scans your Maven + (=pom.xml=) or Gradle (=build.gradle=) configuration to see which agents (like + Lombok or JaCoCo) your project requires. +- /Resolution/: It resolves the exact absolute path to that agent's JAR file + inside your local build cache (e.g., ~$HOME/.m2/repository~ or + ~$HOME/.gradle/caches~). +- /Injection/: It intercepts the startup hook of the JDT Language Server (JDTLS) + and appends the fully constructed ~-javaagent:/path/to/resolved.jar~ string + directly to the LSP server launch command. +- /Global Caching/: To prevent Emacs from freezing while waiting for slow + Maven/Gradle CLI lookups on every single file visit, JAL caches these resolved + paths globally after the first run for instant subsequent startups. ** Features -- /Automatic Detection/: Scans your =pom.xml= or =build.gradle= to find agent - versions. -- /Caching/: Caches the resolved paths per project to avoid slow build system - calls on every startup. -- /Flexible Configuration/: Supports custom JAR names, absolute paths, and agent - parameters. -- /LSP Integration/: Works with both =lsp-java= and =eglot-java= via unobtrusive - advice. -- /Safe/: Uses dynamic let-bindings in the advice -- never permanently mutates - =lsp-java-vmargs= or =eglot-java-eclipse-jdt-args=. -- /Build-system aware/: Only prompts for agent setup when inside a Maven or - Gradle project. +- /LSP Integration/: Works with both =lsp-java= and =eglot-java=. +- /Zero Mutated State (Safe)/: Uses dynamic let-bindings during the injection + process--it never permanently alters or pollutes your global =lsp-java-vmargs= + or =eglot-java-eclipse-jdt-args=. +- /Flexible Configuration/: Moves beyond default lookups to support custom agent + JAR names, absolute fallback paths, and specialized agent parameters. +- /Build-System Aware/: Quietly stays out of your way; it only triggers agent + resolution when you are actively inside a valid Maven or Gradle project. +- /Instant Startups/: Completely bypasses blocking build-tool CLI lookups on + daily file visits by leveraging project-specific path caching. ** Installation *** Prerequisites -JAL integrates with either =lsp-java= or =eglot-java= (note: plain =eglot= is *not* -supported: =eglot-java= is required). At least one of these must be installed: +JAL integrates with either =lsp-java= or =eglot-java= (note: plain =eglot= is +*not* supported: =eglot-java= is required). At least one of these must be +installed: - [[https://github.com/emacs-lsp/lsp-java][lsp-java]] - LSP client for Java using JDTLS - [[https://github.com/yveszoundi/eglot-java][eglot-java]] - eglot-java extension for Emacs -JAL must be loaded *before* =lsp-java= or =eglot-java= so the integration hooks are -registered in time. +Enable the matching integration mode once in your configuration: +=jal-lsp-java-mode= for =lsp-java=, or =jal-eglot-java-mode= for =eglot-java=. + +#+begin_quote +*NOTE*: Both modes are *global* minor modes. Enable them once at the top level +of your configuration (for example in a =use-package :config= block) with +=(jal-lsp-java-mode 1)= or =(jal-eglot-java-mode 1)=. Do *not* add them to a +buffer-local hook such as =java-mode-hook=. Use =-1= to disable. +#+end_quote *** Using ~use-package~ with a local clone @@ -49,7 +80,9 @@ Clone the repository to a local directory and point ~use-package~ at it: (use-package jal :load-path "/path/to/java-agent-loader" :custom - (jal-auto-setup t)) + (jal-auto-setup t) + :config + (jal-lsp-java-mode 1)) ; or (jal-eglot-java-mode 1) #+end_src *** Using ~use-package~ with ~package-vc~ (Emacs 29+) @@ -60,7 +93,9 @@ No clone needed: Emacs fetches the package directly from GitHub: (use-package jal :vc (:url "https://github.com/saulotoledo/java-agent-loader" :rev :newest) :custom - (jal-auto-setup t)) + (jal-auto-setup t) + :config + (jal-lsp-java-mode 1)) ; or (jal-eglot-java-mode 1) #+end_src *** Using straight.el @@ -68,6 +103,7 @@ No clone needed: Emacs fetches the package directly from GitHub: #+begin_src elisp (straight-use-package '(jal :type git :host github :repo "saulotoledo/java-agent-loader")) +(jal-lsp-java-mode 1) ; or (jal-eglot-java-mode 1) #+end_src ** Configuration @@ -80,7 +116,7 @@ this project?"* To run detection automatically without prompting, set ~jal-auto-setup~ to ~t~. To add agents or override known-agent defaults, set ~jal-additional-agents~ -*before* ~lsp-java~ or ~eglot-java~ is loaded. +*before* you enable ~jal-lsp-java-mode~ or ~jal-eglot-java-mode~. #+begin_src elisp (use-package jal @@ -94,12 +130,14 @@ To add agents or override known-agent defaults, set ~jal-additional-agents~ ;; Add an agent not in the known list ("my-custom-agent" :jar-path "/opt/agents/my-agent.jar") - ))) + )) + :config + (jal-lsp-java-mode 1)) ; or (jal-eglot-java-mode 1) #+end_src -JAL hooks into ~lsp-java~ and ~eglot-java~ automatically via -~with-eval-after-load~. No further setup is needed in the ~lsp-java~ or -~eglot-java~ ~use-package~ blocks. +JAL hooks into ~lsp-java~ and ~eglot-java~ once you enable ~jal-lsp-java-mode~ +or ~jal-eglot-java-mode~. These are global modes -- enable one once at the top +level; no further setup is needed in the ~lsp-java~ or ~eglot-java~ blocks. *** Supported Properties for ~jal-additional-agents~ @@ -119,13 +157,15 @@ value ...)~ (override properties). ** Usage with lsp-java -Just load JAL before ~lsp-java~. JAL integrates automatically. +Enable ~jal-lsp-java-mode~ (a global mode) once in your configuration. #+begin_src elisp (use-package jal :ensure t :custom - (jal-additional-agents '(("my-custom-agent")))) ; Optional + (jal-additional-agents '(("my-custom-agent"))) ; Optional + :config + (jal-lsp-java-mode 1)) (use-package lsp-java :ensure t @@ -139,14 +179,16 @@ for each server startup. ** Usage with eglot-java -~eglot-java~ is required (not plain ~eglot~). JAL integrates automatically once -~eglot-java~ is loaded. +~eglot-java~ is required (not plain ~eglot~). Enable ~jal-eglot-java-mode~ (a +global mode) once in your configuration. #+begin_src elisp (use-package jal :ensure t :custom - (jal-additional-agents '(("my-custom-agent")))) ; Optional + (jal-additional-agents '(("my-custom-agent"))) ; Optional + :config + (jal-eglot-java-mode 1)) (use-package eglot-java :ensure t @@ -183,7 +225,9 @@ If your ~pom.xml~ declares Lombok in its dependencies: (use-package jal :ensure t :custom - (jal-auto-setup t)) + (jal-auto-setup t) + :config + (jal-lsp-java-mode 1)) #+end_src JAL will find it automatically -- Lombok is in ~jal-known-agents~, so no custom @@ -196,7 +240,9 @@ configuration is needed. :ensure t :custom (jal-additional-agents - '(("org.jacoco.agent" :params "destfile=build/reports/jacoco.exec")))) + '(("org.jacoco.agent" :params "destfile=build/reports/jacoco.exec"))) + :config + (jal-lsp-java-mode 1)) ; or (jal-eglot-java-mode 1) #+end_src *** Custom Agent with Absolute JAR Path @@ -204,7 +250,9 @@ configuration is needed. #+begin_src elisp (use-package jal :custom - (jal-additional-agents '(("my-agent" :jar-path "/opt/agents/my-agent.jar" :params "config=prod")))) + (jal-additional-agents '(("my-agent" :jar-path "/opt/agents/my-agent.jar" :params "config=prod"))) + :config + (jal-lsp-java-mode 1)) ; or (jal-eglot-java-mode 1) #+end_src *** Interactive Detection diff --git a/jal-client-eglot.el b/jal-client-eglot.el index 0a6bb98..84bc072 100644 --- a/jal-client-eglot.el +++ b/jal-client-eglot.el @@ -89,19 +89,30 @@ Accepts the SERVER argument passed by the hook." (jal-find-and-configure-agents)) ;;;###autoload -(defun jal-eglot-java-setup () - "Configure JAL for eglot-java. -Merges `jal-additional-agents' with the known-agents registry and installs -an around advice on `eglot-java--eclipse-jdt-contact' that dynamically -appends the javaagent vmargs for the current project on every JDTLS startup. -This function is called automatically when eglot-java is loaded." - (setq jal-agents-config (jal--merge-agent-configs jal-additional-agents)) - (setq jal-current-java-key-function #'jal--eglot-current-java-key) - (advice-add 'eglot-java--eclipse-jdt-contact :around #'jal--eglot-java-contact-advice) - (add-hook 'eglot-connect-hook #'jal--eglot-connect-hook-check-interface) - (add-hook 'eglot-connect-hook #'jal--eglot-connect-hook-find-agents) - (add-hook 'jal-agents-detected-hook #'jal--eglot-reconnect) - (setq jal--eglot-java-interface-warning-issued nil)) +(define-minor-mode jal-eglot-java-mode + "Toggle JAL integration with eglot-java (a global minor mode). +This is a GLOBAL mode: enable it once at the top level of your Emacs +configuration with `(jal-eglot-java-mode 1)'. Do not add it to a buffer +hook such as `java-mode-hook'. Use `-1' to disable. When enabled, merges +`jal-additional-agents' with the known-agents registry and installs an +around advice on `eglot-java--eclipse-jdt-contact' that dynamically +appends the javaagent vmargs for the current project on every JDTLS +startup. When disabled, the advice and hooks are removed." + :global t + :group 'jal + (if jal-eglot-java-mode + (progn + (setq jal-agents-config (jal--merge-agent-configs jal-additional-agents)) + (setq jal-current-java-key-function #'jal--eglot-current-java-key) + (advice-add 'eglot-java--eclipse-jdt-contact :around #'jal--eglot-java-contact-advice) + (add-hook 'eglot-connect-hook #'jal--eglot-connect-hook-check-interface) + (add-hook 'eglot-connect-hook #'jal--eglot-connect-hook-find-agents) + (add-hook 'jal-agents-detected-hook #'jal--eglot-reconnect) + (setq jal--eglot-java-interface-warning-issued nil)) + (advice-remove 'eglot-java--eclipse-jdt-contact #'jal--eglot-java-contact-advice) + (remove-hook 'eglot-connect-hook #'jal--eglot-connect-hook-check-interface) + (remove-hook 'eglot-connect-hook #'jal--eglot-connect-hook-find-agents) + (remove-hook 'jal-agents-detected-hook #'jal--eglot-reconnect))) (provide 'jal-client-eglot) ;;; jal-client-eglot.el ends here diff --git a/jal-client-lsp.el b/jal-client-lsp.el index 150b588..6370ac5 100644 --- a/jal-client-lsp.el +++ b/jal-client-lsp.el @@ -78,18 +78,29 @@ Warns at most once per Emacs session to avoid repeat messages on restarts." (jal--warn-interface-changed "lsp-java--ls-command" "lsp-java"))) ;;;###autoload -(defun jal-lsp-java-setup () - "Configure JAL for lsp-java. -Merges `jal-additional-agents' with the known-agents registry and installs -an around advice on `lsp-java--ls-command' that dynamically appends the -javaagent vmargs for the current project on every JDTLS startup. -This function is called automatically when lsp-java is loaded." - (setq jal-agents-config (jal--merge-agent-configs jal-additional-agents)) - (setq jal-current-java-key-function #'jal--lsp-java-current-java-key) - (advice-add 'lsp-java--ls-command :around #'jal--lsp-java-ls-command-advice) - (add-hook 'lsp-after-initialize-hook #'jal--lsp-java-check-interface) - (add-hook 'lsp-after-initialize-hook #'jal-find-and-configure-agents) - (add-hook 'jal-agents-detected-hook #'jal--lsp-java-restart)) +(define-minor-mode jal-lsp-java-mode + "Toggle JAL integration with lsp-java (a global minor mode). +This is a GLOBAL mode: enable it once at the top level of your Emacs +configuration with `(jal-lsp-java-mode 1)'. Do not add it to a buffer +hook such as `java-mode-hook'. Use `-1' to disable. When enabled, merges +`jal-additional-agents' with the known-agents registry and installs an +around advice on `lsp-java--ls-command' that dynamically appends the +javaagent vmargs for the current project on every JDTLS startup. When +disabled, the advice and hooks are removed." + :global t + :group 'jal + (if jal-lsp-java-mode + (progn + (setq jal-agents-config (jal--merge-agent-configs jal-additional-agents)) + (setq jal-current-java-key-function #'jal--lsp-java-current-java-key) + (advice-add 'lsp-java--ls-command :around #'jal--lsp-java-ls-command-advice) + (add-hook 'lsp-after-initialize-hook #'jal--lsp-java-check-interface) + (add-hook 'lsp-after-initialize-hook #'jal-find-and-configure-agents) + (add-hook 'jal-agents-detected-hook #'jal--lsp-java-restart)) + (advice-remove 'lsp-java--ls-command #'jal--lsp-java-ls-command-advice) + (remove-hook 'lsp-after-initialize-hook #'jal--lsp-java-check-interface) + (remove-hook 'lsp-after-initialize-hook #'jal-find-and-configure-agents) + (remove-hook 'jal-agents-detected-hook #'jal--lsp-java-restart))) (provide 'jal-client-lsp) ;;; jal-client-lsp.el ends here diff --git a/jal.el b/jal.el index c0cda88..429ebc6 100644 --- a/jal.el +++ b/jal.el @@ -14,24 +14,30 @@ ;; this program. If not, see . ;; Author: Saulo Toledo -;; Version: 1.0.0 +;; Version: 2.0.0 ;; Package-Requires: ((emacs "28.1")) ;; Keywords: java, languages, tools ;; URL: https://github.com/saulotoledo/java-agent-loader ;;; Commentary: -;; NOTE: This package handles JVM instrumentation agents, NOT artificial -;; intelligence (AI) agents. +;; NOTE: This package handles JVM bytecode instrumentation agents, NOT +;; artificial intelligence (AI) agents. +;; +;; Java Agent Loader (JAL) automates the manual injection of `-javaagent' +;; arguments into the JDT Language Server (JDTLS) process used by development +;; environments like `lsp-java' or `eglot-java'. ;; ;; In the Java ecosystem, a "Java Agent" is a native JVM plugin that uses -;; bytecode instrumentation to modify compiled code on the fly as it loads. It -;; has no relation to AI assistants or LLM workflows. +;; bytecode instrumentation to modify compiled code on the fly as it loads +;; (powering tools like Lombok or JaCoCo). This technology dates back to 2004 +;; (Java 5) and has absolutely no relation to AI assistants, LLMs, or autonomous +;; software agents. ;; -;; Java Agent Loader (JAL) manages the injection of these `-javaagent' arguments -;; for tools like Lombok and JaCoCo into the JDT Language Server (JDTLS) -;; process. It utilizes a global cache to avoid slow Maven or Gradle build tool -;; lookups on every single project startup. +;; JAL bridges the gap between your project's Maven (pom.xml) or Gradle build +;; configuration and your Emacs LSP client. It utilizes global path caching to +;; completely bypass slow build-tool CLI lookups, ensuring your Java projects +;; start instantly on every file visit. ;;; Code: @@ -41,16 +47,10 @@ (require 'jal-build-maven) (require 'jal-build-gradle) -(declare-function jal-lsp-java-setup "jal-client-lsp") -(declare-function jal-eglot-java-setup "jal-client-eglot") - -(with-eval-after-load 'lsp-java - (require 'jal-client-lsp) - (jal-lsp-java-setup)) - -(with-eval-after-load 'eglot-java - (require 'jal-client-eglot) - (jal-eglot-java-setup)) +(autoload 'jal-lsp-java-mode "jal-client-lsp" + "Toggle JAL integration with lsp-java (global minor mode)." t) +(autoload 'jal-eglot-java-mode "jal-client-eglot" + "Toggle JAL integration with eglot-java (global minor mode)." t) ;; ==================================================================== ;; Build System Specific Detection Helpers @@ -191,7 +191,7 @@ and `jal-agents-detected-hook' is run once detection completes." (not (y-or-n-p (format "JAL: Agents already configured for Java at '%s'. Override? " java-key)))) - (user-error "JAL: Detection cancelled")))) + (user-error "JAL: Detection canceled")))) (if jal--detection-in-progress (message "JAL: Detection already in progress.") diff --git a/tests/jal-test.el b/tests/jal-test.el index 77c9e55..f94ed23 100644 --- a/tests/jal-test.el +++ b/tests/jal-test.el @@ -156,26 +156,53 @@ (require 'jal-client-lsp) -(ert-deftest jal-test/lsp-setup-installs-advice () - "`jal-lsp-java-setup' installs the around advice on lsp-java--ls-command." - (jal-lsp-java-setup) +(ert-deftest jal-test/lsp-mode-off-by-default () + "Loading the package does not auto-enable lsp integration." + (should (null (default-value 'jal-lsp-java-mode)))) + +(ert-deftest jal-test/lsp-mode-installs-advice () + "`jal-lsp-java-mode' installs the around advice on lsp-java--ls-command." + (jal-lsp-java-mode 1) (should (advice-member-p #'jal--lsp-java-ls-command-advice 'lsp-java--ls-command)) ;; Clean up - (advice-remove 'lsp-java--ls-command #'jal--lsp-java-ls-command-advice)) - -(ert-deftest jal-test/lsp-setup-does-not-warn-when-function-missing () - "`jal-lsp-java-setup' does not warn at setup time; warning is deferred to the hook." + (jal-lsp-java-mode -1)) + +(ert-deftest jal-test/lsp-mode-off-removes-advice-and-hooks () + "`jal-lsp-java-mode -1' removes the advice and all registered hooks." + (jal-lsp-java-mode 1) + (jal-lsp-java-mode -1) + (should-not (advice-member-p #'jal--lsp-java-ls-command-advice 'lsp-java--ls-command)) + (should-not (memq #'jal--lsp-java-check-interface lsp-after-initialize-hook)) + (should-not (memq #'jal-find-and-configure-agents lsp-after-initialize-hook)) + (should-not (memq #'jal--lsp-java-restart jal-agents-detected-hook))) + +(ert-deftest jal-test/lsp-mode-toggle-is-idempotent () + "Enabling twice does not stack advice or duplicate hooks; disabling cleans up." + (jal-lsp-java-mode 1) + (jal-lsp-java-mode 1) + (should jal-lsp-java-mode) + (should (= 1 (cl-count #'jal--lsp-java-check-interface lsp-after-initialize-hook))) + (jal-lsp-java-mode -1) + (should-not jal-lsp-java-mode) + (should-not (advice-member-p #'jal--lsp-java-ls-command-advice 'lsp-java--ls-command))) + +(ert-deftest jal-test/lsp-orig-callable-after-disable () + "After disabling, lsp-java--ls-command still returns the bare command." + (jal-lsp-java-mode 1) + (jal-lsp-java-mode -1) + (should (equal '("java" "-jar" "jdtls.jar") (lsp-java--ls-command)))) + +(ert-deftest jal-test/lsp-mode-does-not-warn-when-function-missing () + "`jal-lsp-java-mode' does not warn at enable time; warn deferred to hook." (cl-letf (((symbol-function 'lsp-java--ls-command) nil)) (let ((warned nil)) (cl-letf (((symbol-function 'display-warning) (lambda (_type msg &rest _) (setq warned msg)))) - (jal-lsp-java-setup)) - ;; No warning should be emitted during setup itself. + (jal-lsp-java-mode 1)) + ;; No warning should be emitted during enable itself. (should (null warned)) (should (memq #'jal--lsp-java-check-interface lsp-after-initialize-hook)))) - (remove-hook 'lsp-after-initialize-hook #'jal-find-and-configure-agents) - (remove-hook 'lsp-after-initialize-hook #'jal--lsp-java-check-interface) - (advice-remove 'lsp-java--ls-command #'jal--lsp-java-ls-command-advice)) + (jal-lsp-java-mode -1)) (ert-deftest jal-test/lsp-check-interface-warns-when-function-missing () "`jal--lsp-java-check-interface' warns when lsp-java--ls-command is not defined." @@ -227,7 +254,7 @@ (defun eglot-java--eclipse-jdt-contact (_interactive) "Return the contact specification for connecting to the JDTLS server with Eglot. -The return value is a cons cell as expected by Eglot’s server connection logic." +The return value is a cons cell as expected by Eglot's server connection logic." (cons 'eglot-java-eclipse-jdt '("java" "-jar" "jdtls.jar"))) (defun eglot-java--find-java-program-from-alternatives () @@ -237,28 +264,56 @@ Returns the first matching executable in the current PATH." (require 'jal-client-eglot) -(ert-deftest jal-test/eglot-setup-installs-advice () - "`jal-eglot-java-setup' installs the around advice on eglot-java--eclipse-jdt-contact." - (jal-eglot-java-setup) - (should (advice-member-p #'jal--eglot-java-contact-advice 'eglot-java--eclipse-jdt-contact)) - (advice-remove 'eglot-java--eclipse-jdt-contact #'jal--eglot-java-contact-advice)) +(ert-deftest jal-test/eglot-mode-off-by-default () + "Loading the package does not auto-enable eglot integration." + (should (null (default-value 'jal-eglot-java-mode)))) -(ert-deftest jal-test/eglot-setup-does-not-warn-when-function-missing () - "`jal-eglot-java-setup' does not warn at setup time; warning is deferred to the hook." +(ert-deftest jal-test/eglot-mode-installs-advice () + "`jal-eglot-java-mode' installs a proper eglot-java around advice." + (jal-eglot-java-mode 1) + (should (advice-member-p #'jal--eglot-java-contact-advice 'eglot-java--eclipse-jdt-contact)) + (jal-eglot-java-mode -1)) + +(ert-deftest jal-test/eglot-mode-off-removes-advice-and-hooks () + "`jal-eglot-java-mode -1' removes the advice and all registered hooks." + (jal-eglot-java-mode 1) + (jal-eglot-java-mode -1) + (should-not (advice-member-p #'jal--eglot-java-contact-advice 'eglot-java--eclipse-jdt-contact)) + (should-not (memq #'jal--eglot-connect-hook-check-interface eglot-connect-hook)) + (should-not (memq #'jal--eglot-connect-hook-find-agents eglot-connect-hook)) + (should-not (memq #'jal--eglot-reconnect jal-agents-detected-hook))) + +(ert-deftest jal-test/eglot-mode-toggle-is-idempotent () + "Enabling twice does not stack advice or duplicate hooks; disabling cleans up." + (jal-eglot-java-mode 1) + (jal-eglot-java-mode 1) + (should jal-eglot-java-mode) + (should (= 1 (cl-count #'jal--eglot-connect-hook-check-interface eglot-connect-hook))) + (jal-eglot-java-mode -1) + (should-not jal-eglot-java-mode) + (should-not (advice-member-p #'jal--eglot-java-contact-advice 'eglot-java--eclipse-jdt-contact))) + +(ert-deftest jal-test/eglot-orig-callable-after-disable () + "After disabling, eglot-java--eclipse-jdt-contact still returns the bare contact." + (jal-eglot-java-mode 1) + (jal-eglot-java-mode -1) + (should (equal (cons 'eglot-java-eclipse-jdt '("java" "-jar" "jdtls.jar")) + (eglot-java--eclipse-jdt-contact nil)))) + +(ert-deftest jal-test/eglot-mode-does-not-warn-when-function-missing () + "`jal-eglot-java-mode' does not warn at enable time; warn deferred to the hook." (cl-letf (((symbol-function 'eglot-java--eclipse-jdt-contact) nil)) (let ((warned nil)) (cl-letf (((symbol-function 'display-warning) (lambda (_type msg &rest _) (setq warned msg)))) - (jal-eglot-java-setup)) - ;; No warning should be emitted during setup itself. + (jal-eglot-java-mode 1)) + ;; No warning should be emitted during enable itself. (should (null warned)) (should (memq #'jal--eglot-connect-hook-check-interface eglot-connect-hook)))) - (remove-hook 'eglot-connect-hook #'jal--eglot-connect-hook-find-agents) - (remove-hook 'eglot-connect-hook #'jal--eglot-connect-hook-check-interface) - (advice-remove 'eglot-java--eclipse-jdt-contact #'jal--eglot-java-contact-advice)) + (jal-eglot-java-mode -1)) (ert-deftest jal-test/eglot-check-interface-warns-when-function-missing () - "`jal--eglot-java-check-interface' warns when eglot-java--eclipse-jdt-contact is not defined." + "`jal--eglot-java-check-interface' warns when function is not defined." (let ((jal--eglot-java-interface-warning-issued nil)) (cl-letf (((symbol-function 'eglot-java--eclipse-jdt-contact) nil)) (let ((warned nil)) @@ -269,7 +324,7 @@ Returns the first matching executable in the current PATH." (should (string-match-p "eglot-java--eclipse-jdt-contact" warned)))))) (ert-deftest jal-test/eglot-check-interface-silent-when-function-present () - "`jal--eglot-java-check-interface' is silent when eglot-java--eclipse-jdt-contact exists." + "`jal--eglot-java-check-interface' is silent when funtion exists." (let ((jal--eglot-java-interface-warning-issued nil) (warned nil)) (cl-letf (((symbol-function 'display-warning) @@ -313,10 +368,10 @@ Returns the first matching executable in the current PATH." (result (jal--gradle-parse-init-output line))) (should (= 1 (length result))) (let ((entry (car result))) - (should (equal "lombok" (nth 0 entry))) - (should (equal "org.projectlombok" (nth 1 entry))) - (should (equal "1.18.30" (nth 2 entry))) - (should (equal "/repo/lombok-1.18.30.jar" (nth 3 entry)))))) + (should (equal "lombok" (nth 0 entry))) + (should (equal "org.projectlombok" (nth 1 entry))) + (should (equal "1.18.30" (nth 2 entry))) + (should (equal "/repo/lombok-1.18.30.jar" (nth 3 entry)))))) (ert-deftest jal-test/gradle-parse-ignores-noise () "Lines not starting with JAL_ARTIFACT are ignored."