Skip to content

Commit fbd04cf

Browse files
committed
Move dap-java into lsp-java
1 parent caafb6c commit fbd04cf

File tree

2 files changed

+359
-3
lines changed

2 files changed

+359
-3
lines changed

dap-java.el

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
;;; dap-java.el --- Debug Adapter Protocol mode for Java -*- lexical-binding: t; -*-
2+
3+
;; Copyright (C) 2018 Ivan Yonchovski
4+
5+
;; Author: Ivan Yonchovski <[email protected]>
6+
;; Keywords: languages
7+
8+
;; This program is free software; you can redistribute it and/or modify
9+
;; it under the terms of the GNU General Public License as published by
10+
;; the Free Software Foundation, either version 3 of the License, or
11+
;; (at your option) any later version.
12+
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+
18+
;; You should have received a copy of the GNU General Public License
19+
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
21+
;; URL: https://github.com/yyoncho/dap-mode
22+
;; Package-Requires: ((emacs "25.1") (dash "2.14.1") (lsp-mode "4.0") (lsp-java "0.1"))
23+
;; Version: 0.2
24+
;; DAP Adapter for java
25+
26+
;;; Commentary:
27+
28+
;;; Code:
29+
30+
(require 'lsp-java)
31+
(require 'dap-mode)
32+
33+
(defvar dap-java--classpath-separator (if (string= system-type "windows-nt")
34+
";"
35+
":"))
36+
37+
(defvar dap-java--var-format (if (string= system-type "windows-nt")
38+
"%%%s%%"
39+
"$%s"))
40+
41+
;; Set to non-nil to use TestNG instead of the default JUnit
42+
(defvar dap-java-use-testng nil)
43+
44+
(defcustom dap-java-java-command "java"
45+
"Path of the java executable."
46+
:group 'dap-java
47+
:type 'string)
48+
49+
(defcustom dap-java-compile-port 33000
50+
"The debug port which will be used for compile/attach configuration.
51+
If the port is taken, DAP will try the next port."
52+
:group 'dap-java
53+
:type 'number)
54+
55+
(defcustom dap-java-test-runner
56+
(expand-file-name (locate-user-emacs-file "eclipse.jdt.ls/test-runner/junit-platform-console-standalone.jar"))
57+
"DAP Java test runner."
58+
:group 'dap-java-java
59+
:type 'file)
60+
61+
62+
(defcustom dap-java-testng-report-dir "build/test-output"
63+
"The directory where TestNG reports will be generated."
64+
:group 'dap-java-java
65+
:type 'string)
66+
67+
68+
(defcustom dap-java-build 'ask
69+
"Perform build before running project behaviour."
70+
:group 'dap-java
71+
:type '(choice (const ask)
72+
(const always)
73+
(const never)))
74+
75+
(defcustom dap-java-hot-reload 'always
76+
"How to perfor hot reload."
77+
:group 'dap-java
78+
:type '(choice (const always)
79+
(const never)))
80+
81+
(defcustom dap-java-test-additional-args ()
82+
"Additional arguments for JUnit standalone runner."
83+
:group 'dap-java
84+
:type '(list string))
85+
86+
(defcustom dap-java-default-debug-port 1044
87+
"Default debug port."
88+
:group 'dap-java
89+
:type 'number)
90+
91+
(eval-and-compile
92+
(lsp-interface
93+
(java:MainClass (:mainClass :projectName))))
94+
95+
(defun dap-java-test-class ()
96+
"Get class FDQN."
97+
(-if-let* ((symbols (lsp--get-document-symbols))
98+
(package-name (-some->> symbols
99+
(-first (-lambda ((&DocumentSymbol :kind)) (= kind lsp/symbol-kind-package)))
100+
lsp:document-symbol-name))
101+
(class-name (->> symbols
102+
(--first (= (lsp:document-symbol-kind it) lsp/symbol-kind-class))
103+
lsp:document-symbol-name)))
104+
(concat package-name "." class-name)
105+
(user-error "No class found")))
106+
107+
(defun dap-java-test-method-at-point ()
108+
"Get method at point."
109+
(-let* ((symbols (lsp--get-document-symbols))
110+
(package-name (-some->> symbols
111+
(-first (-lambda ((&DocumentSymbol :kind)) (= kind lsp/symbol-kind-package)))
112+
lsp:document-symbol-name)))
113+
(or (->> symbols
114+
(-keep (-lambda ((&DocumentSymbol :children? :kind :name class-name))
115+
(and (= kind lsp/symbol-kind-class)
116+
(seq-some
117+
(-lambda ((&DocumentSymbol :kind :range :selection-range))
118+
(-let (((beg . end) (lsp--range-to-region range)))
119+
(and (= lsp/symbol-kind-method kind) (<= beg (point) end)
120+
(concat package-name "." class-name "#"
121+
(lsp-region-text selection-range)))))
122+
children?))))
123+
(cl-first))
124+
(user-error "No method at point"))))
125+
126+
(defun dap-java--select-main-class ()
127+
"Select main class from the current workspace."
128+
(let* ((main-classes (with-lsp-workspace (lsp-find-workspace 'jdtls)
129+
(lsp-send-execute-command "vscode.java.resolveMainClass")))
130+
(main-classes-count (length main-classes))
131+
current-class)
132+
(cond
133+
((= main-classes-count 0) (error "Unable to find main class.
134+
Please check whether the server is configured propertly"))
135+
((= main-classes-count 1) (cl-first main-classes))
136+
((setq current-class (--first (string= buffer-file-name (lsp-get it :filePath))
137+
main-classes))
138+
current-class)
139+
(t (dap--completing-read "Select main class to run: "
140+
main-classes
141+
(lambda (it)
142+
(format "%s(%s)"
143+
(lsp-get it :mainClass)
144+
(lsp-get it :projectName)))
145+
nil
146+
t)))))
147+
(defun dap-java--populate-launch-args (conf)
148+
"Populate CONF with launch related configurations."
149+
(when (not (and (plist-get conf :mainClass)
150+
(plist-get conf :projectName)))
151+
(-let [(&java:MainClass :main-class :project-name) (dap-java--select-main-class)]
152+
(setq conf (plist-put conf :mainClass main-class))
153+
(plist-put conf :projectName project-name)))
154+
155+
(-let [(&plist :mainClass main-class :projectName project-name) conf]
156+
(-> conf
157+
(dap--put-if-absent :args "")
158+
(dap--put-if-absent :cwd (lsp-java--get-root))
159+
(dap--put-if-absent :stopOnEntry :json-false)
160+
(dap--put-if-absent :host "localhost")
161+
(dap--put-if-absent :console "internalConsole")
162+
(dap--put-if-absent :request "launch")
163+
(dap--put-if-absent :modulePaths (vector))
164+
(dap--put-if-absent :classPaths
165+
(or (cl-second
166+
(with-lsp-workspace (lsp-find-workspace 'jdtls)
167+
(lsp-send-execute-command
168+
"vscode.java.resolveClasspath"
169+
(vector main-class project-name))))
170+
(error "Unable to resolve classpath")))
171+
(dap--put-if-absent :name (format "%s (%s)"
172+
(if (string-match ".*\\.\\([[:alnum:]_]*\\)$" main-class)
173+
(match-string 1 main-class)
174+
main-class)
175+
project-name)))))
176+
177+
(defun dap-java--populate-attach-args (conf)
178+
"Populate attach arguments.
179+
CONF - the startup configuration."
180+
(dap--put-if-absent conf :hostName (read-string "Enter host: " "localhost"))
181+
(dap--put-if-absent conf :port (string-to-number (read-string "Enter port: "
182+
(number-to-string dap-java-default-debug-port))))
183+
(dap--put-if-absent conf :host "localhost")
184+
(dap--put-if-absent conf :name (format "%s(%s)"
185+
(plist-get conf :host)
186+
(plist-get conf :port)))
187+
conf)
188+
189+
(defun dap-java--populate-compile-attach-args (conf)
190+
"Populate the CONF for running compile/attach.
191+
Populate the arguments like normal 'Launch' request but then
192+
initiate `compile' and attach to the process."
193+
(dap-java--populate-launch-args conf)
194+
(-let* (((&plist :mainClass :projectName :classPaths classpaths) conf)
195+
(port (dap--find-available-port))
196+
(program-to-start (format "%s -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=%s,quiet=y -cp %s %s"
197+
dap-java-java-command
198+
port
199+
(format dap-java--var-format "CLASSPATH_ARGS")
200+
mainClass)))
201+
(dap-java--populate-attach-args
202+
(list :type "java"
203+
:request "attach"
204+
:hostName "localhost"
205+
:projectName projectName
206+
:host "localhost"
207+
:wait-for-port t
208+
:program-to-start program-to-start
209+
:port port
210+
:environment-variables `(("CLASSPATH_ARGS" . ,(s-join dap-java--classpath-separator classpaths)))))))
211+
212+
(defun dap-java--populate-default-args (conf)
213+
"Populate all of the fields that are not present in CONF."
214+
(setq conf (plist-put conf :type "java"))
215+
216+
(setq conf (pcase (plist-get conf :request)
217+
("launch" (dap-java--populate-launch-args conf))
218+
("attach" (dap-java--populate-attach-args conf))
219+
("compile_attach" (dap-java--populate-compile-attach-args conf))
220+
(_ (dap-java--populate-launch-args conf))))
221+
(plist-put conf :debugServer (with-lsp-workspace (lsp-find-workspace 'jdtls)
222+
(lsp-send-execute-command "vscode.java.startDebugSession")))
223+
(plist-put conf :__sessionId (number-to-string (float-time)))
224+
conf)
225+
226+
(defun dap-java-debug (debug-args)
227+
"Start debug session with DEBUG-ARGS."
228+
(interactive (list (dap-java--populate-default-args nil)))
229+
(dap-start-debugging debug-args))
230+
231+
(defvar testng-report-directory)
232+
233+
(defun dap-java--run-unit-test-command (runner run-method?)
234+
"Run debug test with the following arguments.
235+
RUNNER is the test executor. RUN-METHOD? when t it will try to
236+
run the surrounding method. Otherwise it will run the surronding
237+
test."
238+
(-let* ((to-run (if run-method?
239+
(dap-java-test-method-at-point)
240+
(dap-java-test-class)))
241+
(test-class-name (cl-first (s-split "#" to-run)))
242+
(class-path (->> (with-lsp-workspace (lsp-find-workspace 'jdtls)
243+
(lsp-send-execute-command "vscode.java.resolveClasspath"
244+
(vector test-class-name nil)))
245+
cl-second
246+
(s-join dap-java--classpath-separator)))
247+
(prog-list (if dap-java-use-testng
248+
(cl-list* runner
249+
"-cp" (format dap-java--var-format "JUNIT_CLASS_PATH")
250+
"org.testng.TestNG"
251+
"-d" testng-report-directory
252+
(if (and (s-contains? "#" to-run) run-method?) "-methods" "-testclass")
253+
(if run-method? (s-replace "#" "." to-run) test-class-name)
254+
dap-java-test-additional-args)
255+
(cl-list* runner "-jar" dap-java-test-runner
256+
"-cp" (format dap-java--var-format "JUNIT_CLASS_PATH")
257+
(if (and (s-contains? "#" to-run) run-method?) "-m" "-c")
258+
(if run-method? to-run test-class-name)
259+
dap-java-test-additional-args))))
260+
(list :program-to-start (s-join " " prog-list)
261+
:environment-variables `(("JUNIT_CLASS_PATH" . ,class-path))
262+
:name to-run
263+
:cwd (lsp-java--get-root))))
264+
265+
(defun dap-java-run-test-method ()
266+
"Run JUnit test.
267+
If there is no method under cursor it will fallback to test class."
268+
(interactive)
269+
(-> (dap-java--run-unit-test-command dap-java-java-command t)
270+
(plist-put :skip-debug-session t)
271+
dap-start-debugging))
272+
273+
(defun dap-java-debug-test-method (port)
274+
"Debug JUnit test.
275+
If there is no method under cursor it will fallback to test class.
276+
PORT is the port that is going to be used for starting and
277+
attaching to the test."
278+
(interactive (list (dap--find-available-port)))
279+
(-> (list :type "java"
280+
:request "attach"
281+
:hostName "localhost"
282+
:port port
283+
:wait-for-port t)
284+
(append (dap-java--run-unit-test-command
285+
(format "%s -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%s"
286+
dap-java-java-command
287+
port)
288+
t))
289+
dap-debug))
290+
291+
(defun dap-java-run-test-class ()
292+
"Run JUnit test."
293+
(interactive)
294+
(-> (dap-java--run-unit-test-command dap-java-java-command nil)
295+
(plist-put :skip-debug-session t)
296+
dap-start-debugging))
297+
298+
(defun dap-java-debug-test-class (port)
299+
"Debug JUnit test class.
300+
301+
PORT is the port that is going to be used for starting and
302+
attaching to the test."
303+
(interactive (list (dap--find-available-port)))
304+
(dap-debug
305+
(append (list :type "java"
306+
:request "attach"
307+
:hostName "localhost"
308+
:port port
309+
:wait-for-port t)
310+
(dap-java--run-unit-test-command
311+
(format "%s -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%s"
312+
dap-java-java-command
313+
port)
314+
nil))))
315+
316+
(cl-defmethod dap-handle-event ((_event (eql hotcodereplace)) session _params)
317+
"Handle DAP events for SESSION."
318+
(when (eq dap-java-hot-reload 'always)
319+
(-let [(&hash "changedClasses" classes) (dap-request session "redefineClasses")]
320+
(if classes
321+
(lsp--info "Reloaded the following classes: %s." classes)
322+
(lsp--warn "There are no classes to redefine.")))))
323+
324+
(dap-register-debug-provider "java" #'dap-java--populate-default-args)
325+
(dap-register-debug-template "Java Run Configuration"
326+
(list :type "java"
327+
:request "launch"
328+
:args ""
329+
:cwd nil
330+
:stopOnEntry :json-false
331+
:host "localhost"
332+
:request "launch"
333+
:modulePaths (vector)
334+
:classPaths nil
335+
:projectName nil
336+
:mainClass nil))
337+
(dap-register-debug-template "Java Run Configuration (compile/attach)"
338+
(list :type "java"
339+
:request "compile_attach"
340+
:args ""
341+
:cwd nil
342+
:host "localhost"
343+
:request "launch"
344+
:modulePaths (vector)
345+
:classPaths nil
346+
:name "Run"
347+
:projectName nil
348+
:mainClass nil))
349+
(dap-register-debug-template "Java Attach"
350+
(list :type "java"
351+
:request "attach"
352+
:hostName "localhost"
353+
:port nil))
354+
355+
;;;###autoload(with-eval-after-load 'lsp-java (require 'dap-java))
356+
357+
(provide 'dap-java)
358+
;;; dap-java.el ends here

lsp-java.el

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
;; Version: 3.0
44

5-
;; Package-Requires: ((emacs "25.1") (lsp-mode "6.0") (markdown-mode "2.3") (dash "2.14.1") (f "0.20.0") (ht "2.0") (dash-functional "1.2.0") (request "0.3.0") (treemacs "2.5"))
5+
;; Package-Requires: ((emacs "25.1") (lsp-mode "6.0") (markdown-mode "2.3") (dash "2.14.1") (f "0.20.0") (ht "2.0") (dash-functional "1.2.0") (request "0.3.0") (treemacs "2.5") (dap-mode "0.5"))
66
;; Keywords: languague, tools
77
;; URL: https://github.com/emacs-lsp/lsp-java
88

@@ -1548,8 +1548,6 @@ current symbol."
15481548

15491549

15501550

1551-
;;;###autoload(with-eval-after-load 'lsp-mode (require 'lsp-java))
1552-
15531551
(provide 'lsp-java)
15541552
;;; lsp-java.el ends here
15551553

0 commit comments

Comments
 (0)