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."