20
20
21
21
; ;; Code:
22
22
23
+ (require 'cl-lib )
24
+ (require 'dash )
25
+ (require 'ht )
26
+ (require 'json )
27
+
23
28
(require 'dap-mode )
24
29
(require 'dap-utils )
25
30
@@ -34,43 +39,243 @@ Link: https://marketplace.visualstudio.com/items?itemName=webfreak.debug ."
34
39
:group 'dap-js
35
40
:type '(repeat string))
36
41
37
- (defun dap-js-setup (&optional forced )
38
- " Downloading webfreak.debug to path specified.
39
- With prefix, FORCED to redownload the extension."
40
- (interactive " P" )
41
- (unless (and (not forced) (file-exists-p dap-js-path))
42
- (lsp-download-install
43
- (lambda (&rest _ ) (lsp--info " Downloaded extension!" ))
44
- (lambda (error ) (lsp--error " Failed Downloaded extension %s!" error ))
45
- :url (lsp--find-latest-gh-release-url
46
- " https://api.github.com/repos/microsoft/vscode-js-debug/releases/latest"
47
- " js-debug-dap" )
48
- :store-path dap-js-path
49
- :decompress :targz )))
42
+ (defcustom dap-js-output-telemetry t
43
+ " Output telemetry data from js-debug server if non-nil."
44
+ :group 'dap-js
45
+ :type 'boolean )
46
+
47
+ (defcustom dap-js-extension-version " latest"
48
+ " The version of the github release found at
49
+ https://github.com/microsoft/vscode-js-debug/releases"
50
+ :group 'dap-js
51
+ :type 'string )
52
+
53
+ (dap-utils-github-extension-setup-function " dap-js" " microsoft" " vscode-js-debug"
54
+ dap-js-extension-version
55
+ dap-js-path
56
+ #'dap-js-extension-build )
57
+
58
+ (defun dap-js-extension-build ()
59
+ " Callback from setup function in order to install extension node deps and compile."
60
+ (message " Building ms-vscode.js-debug in %s directory. " dap-js-path)
61
+ (let ((buf (get-buffer-create " *dap-js extension build*" ))
62
+ (default-directory (concat dap-js-path " /extension" )))
63
+ (async-shell-command
64
+ " npm install --sav-dev --force; npm run compile -- dapDebugServer" buf buf)))
65
+
66
+ (cl-defun dap-js-extension-update (&optional (ask-upgrade t ))
67
+ " Check for update, and if `ask-upgrade' arg is non-nil will prompt user to upgrade."
68
+ (interactive )
69
+ (let* ((url (format dap-utils-github-extension-releases-info-url " microsoft"
70
+ " vscode-js-debug" " latest" ))
71
+ (cur-version
72
+ (let ((file (f-join dap-js-path " extension/package.json" )))
73
+ (when (file-exists-p file)
74
+ (with-temp-buffer
75
+ (insert-file-contents file)
76
+ (goto-char (point-min ))
77
+ (cdr (assoc 'version (json-read )))))))
78
+ (latest-version
79
+ (let ((inhibit-message dap-inhibit-io))
80
+ (with-current-buffer
81
+ (if-let ((buf (url-retrieve-synchronously url t t 10 )))
82
+ buf ; returned
83
+ (progn
84
+ ; ; Probably timeout
85
+ (message " Problem getting latest version from: %s " url)
86
+ (generate-new-buffer " *dap-js-temp*" )))
87
+ (if (/= (point-max ) 1 )
88
+ (progn
89
+ (goto-char (point-min ))
90
+ (re-search-forward " ^$" )
91
+ (substring (cdr (assoc 'tag_name (json-read ))) 1 ))
92
+ (progn
93
+ (kill-buffer )
94
+ cur-version))))))
95
+ (if (string= cur-version latest-version)
96
+ (when ask-upgrade
97
+ (message " ms-vscode.js-debug extension is up to date at version: %s "
98
+ latest-version))
99
+ (let ((msg (format " Newer version (%s ) of vscode/ms-vscode.js-debug exists than \
100
+ currently installed version (%s ). " latest-version cur-version)))
101
+ (if ask-upgrade
102
+ (when (y-or-n-p (concat msg " Do you want to upgrade now?" ))
103
+ (dap-js-setup t ))
104
+ (message " %s Upgrade with `M-x dap-js-extension-update'" msg))))))
105
+
106
+ ; ; Check extension version when loading, and give a message about upgrading.
107
+ (dap-js-extension-update nil )
50
108
51
109
(defun dap-js--populate-start-file-args (conf )
52
- " Populate CONF with the required arguments."
53
- (let ((port (dap--find-available-port)))
54
- (-> conf
55
- (append
56
- (list :debugServer port
57
- :host " localhost"
58
- :type " pwa-node"
59
- :program-to-start (concat (s-join " " dap-js-debug-program)
60
- " "
61
- (number-to-string port))))
62
- (dap--put-if-absent :cwd default-directory)
63
- (dap--put-if-absent :name " Node Debug" ))))
110
+ " Load up the start config CONF for the debug adapter from launch.json, and default
111
+ required attributes if missing. See full options:
112
+ `https://github.com/microsoft/vscode-js-debug/blob/main/OPTIONS.md' "
113
+ (dap--put-if-absent conf :type " chrome" )
114
+ (dap--put-if-absent conf :cwd (lsp-workspace-root))
115
+ (dap--put-if-absent conf :request " launch" )
116
+ (dap--put-if-absent conf :console " internalConsole" )
117
+ (dap--put-if-absent conf :name (concat (plist-get conf :type ) " -js-debug" ))
118
+ (let ((debug-port (dap--find-available-port)))
119
+ (dap--put-if-absent conf :host " localhost" )
120
+ (dap--put-if-absent conf :debugServer debug-port)
121
+ (dap--put-if-absent conf :debugPort debug-port)
122
+ (dap--put-if-absent conf :program-to-start
123
+ (if (not (file-exists-p dap-js-path))
124
+ (error " DAP program path: %s does not exist! " dap-js-path)
125
+ (format " %s %s %s "
126
+ (mapconcat 'identity dap-js-debug-program " " )
127
+ (plist-get conf :debugPort )
128
+ (plist-get conf :host )))))
129
+ (if (plist-member conf :url )
130
+ (progn
131
+ ; ;(plist-put conf :mode "url")
132
+ (dap--put-if-absent conf :url (read-string
133
+ " Browse url: "
134
+ " http://localhost:3000" t ))
135
+ (dap--put-if-absent conf :webRoot (lsp-workspace-root))))
136
+ (if (plist-member conf :file )
137
+ (if (plist-get conf :url )
138
+ (error " Both \" file\" and \" url\" properties are set in launch.json. \
139
+ Choose one. " )
140
+ (progn
141
+ (plist-put conf :mode " file" )
142
+ (dap--put-if-absent conf :file
143
+ (read-file-name " Select the file to open in the browser:"
144
+ nil (buffer-file-name ) t )))))
145
+ (if (plist-member conf :program )
146
+ (dap--put-if-absent conf :program (read-file-name
147
+ " Select the Node.js program to run: "
148
+ nil (buffer-file-name ) t )))
149
+ (when (string= " node-terminal" (plist-get conf :type ))
150
+ (error " In launch.json \" node-terminal\" debug type is currently not supported. " ))
151
+ (when (string= " integratedTerminal" (plist-get conf :console ))
152
+ (error " In launch.json \" console\" :\" integratedTerminal\" not supported at this \
153
+ time, use \" console\" :\" internalConsole\" instead " ))
154
+ (unless dap-inhibit-io
155
+ (message " dap-js---populate-start-file-args: %s " conf))
156
+ conf)
64
157
158
+ (dap-register-debug-provider " node" #'dap-js--populate-start-file-args )
159
+ (dap-register-debug-provider " node-terminal" #'dap-js--populate-start-file-args )
160
+ (dap-register-debug-provider " chrome" #'dap-js--populate-start-file-args )
161
+ (dap-register-debug-provider " msedge" #'dap-js--populate-start-file-args )
65
162
(dap-register-debug-provider " pwa-node" #'dap-js--populate-start-file-args )
163
+ (dap-register-debug-provider " pwa-node" #'dap-js--populate-start-file-args )
164
+ (dap-register-debug-provider " pwa-chrome" #'dap-js--populate-start-file-args )
165
+ (dap-register-debug-provider " pwa-msedge" #'dap-js--populate-start-file-args )
166
+
167
+ (dap-register-debug-template " Node.js Launch Program"
168
+ (list :type " node"
169
+ :cwd nil
170
+ :request " launch"
171
+ :program nil
172
+ :name " Node.js Launch Program" ))
173
+
174
+ (dap-register-debug-template " Chrome Launch File"
175
+ (list :type " chrome"
176
+ :cwd nil
177
+ :request " launch"
178
+ :file nil
179
+ :name " Chrome Launch File" ))
180
+
181
+ (dap-register-debug-template " Chrome Launch URL"
182
+ (list :type " chrome"
183
+ :cwd nil
184
+ :request " launch"
185
+ :webRoot nil
186
+ :url nil
187
+ :name " Chrome Launch URL" ))
188
+
189
+
190
+ (add-hook 'dap-session-created-hook #'dap-js--session-created )
191
+ (defun dap-js--session-created (debug-session )
192
+ " Set up so that processes won't ask about closing."
193
+ (when-let (proc (dap--debug-session-program-proc debug-session))
194
+ (set-process-query-on-exit-flag proc nil )))
195
+
196
+ (defun dap-js--output-filter-function (debug-session event )
197
+ " Output event data, including for vscode-js-debug, some useful telemetry data.
198
+ Future can do something more with the telemetry data than just printing."
199
+ (-let [(&hash " seq" " event" event-type " body" ) event]
200
+ (if (hash-table-p body)
201
+ (progn
202
+ (if (and (bound-and-true-p dap-js-output-telemetry)
203
+ (string= (gethash " category" body) " telemetry" ))
204
+ (dap--print-to-output-buffer
205
+ debug-session (concat (dap--json-encode body) " \n " ))
206
+ (dap--print-to-output-buffer
207
+ debug-session (concat (dap--output-buffer-format body) " \n " )))))))
208
+
209
+ (add-hook 'dap-terminated-hook #'dap-js--term-parent )
210
+ (defun dap-js--term-parent (debug-session )
211
+ " Kill off parent process when child is disconnected."
212
+ (if (eq debug-session (if (boundp 'parent-debug-session ) parent-debug-session nil ))
213
+ (progn
214
+ (when-let (proc (dap--debug-session-program-proc debug-session))
215
+ (when (process-live-p proc)
216
+ (makunbound 'parent-debug-session )
217
+ (set-process-query-on-exit-flag proc nil )
218
+ (with-current-buffer (process-buffer proc)
219
+ ; ; Switching mode, prevents triggering to open err file after killing proc
220
+ (shell-script-mode )
221
+ (kill-buffer ))
222
+ (dap-delete-session debug-session)))))
223
+ (kill-buffer (dap--debug-session-output-buffer debug-session)))
66
224
67
- (dap-register-debug-template
68
- " Node Run Configuration (new)"
69
- (list :type " pwa-node"
70
- :cwd nil
71
- :request " launch"
72
- :program nil
73
- :name " Node::Run" ))
225
+ (add-hook 'dap-executed-hook #'dap-js--reverse-request-handler )
226
+ (defun dap-js--reverse-request-handler (debug-session command )
227
+ " Callback hook to get messages from dap-mode reverse requests."
228
+ ; ;This is set with `add-hook' above.
229
+ (unless dap-inhibit-io
230
+ (message " dap-js--reverse-request-handler -> command: %s " command))
231
+ (pcase command
232
+ ((guard (string= command " startDebugging" ))
233
+ ; ; Assume current session now parent requesting start debugging in child session
234
+ (setq parent-debug-session debug-session)
235
+ (-let [(&hash " seq" " command" " arguments"
236
+ (&hash " request" " configuration"
237
+ (&hash? " type" " __pendingTargetId" )))
238
+ (dap--debug-session-metadata debug-session)]
239
+ (-let (((&plist :mode :url :file :webroot :program :outputCapture
240
+ :skipFiles :timeout :host :name :debugPort )
241
+ (dap--debug-session-launch-args debug-session))
242
+ (conf `(:request , request )))
243
+ ; ; DAP Spec says not to include client variables to start child, including type
244
+ ; ;(plist-put conf :type type)
245
+ (plist-put conf :name (concat type " -" command))
246
+ (plist-put conf :__pendingTargetId __pendingTargetId)
247
+ (plist-put conf :outputCapture outputCapture)
248
+ (plist-put conf :skipFiles skipFiles)
249
+ (plist-put conf :timeout timeout)
250
+ (plist-put conf :host host)
251
+ (plist-put conf :debugServer debugPort)
252
+ (plist-put conf :debugPort debugPort)
253
+ (if (or (string= " pwa-node" type) (string= " node" type))
254
+ (plist-put conf :program program)
255
+ (progn
256
+ (if (string= mode " file" )
257
+ (plist-put conf :file file)
258
+ (progn
259
+ (plist-put conf :url url)
260
+ (plist-put conf :webroot webroot)))))
261
+ (unless dap-inhibit-io
262
+ (message " dap-js startDebugging conf: %s " conf))
263
+ (dap-start-debugging-noexpand conf)
264
+ ; ; Remove child session if stored in list of recent/last configurations to
265
+ ; ; allow `dap-debug-last' to work by getting parent not child.
266
+ (when-let ((last-conf (cdr (cl-first dap--debug-configuration)))
267
+ (_ptid-equal (string= __pendingTargetId
268
+ (plist-get last-conf :__pendingTargetId ))))
269
+ (pop dap--debug-configuration))
270
+ ; ; success
271
+ (dap--send-message (dap--make-success-response seq command)
272
+ (dap--resp-handler) debug-session))))
273
+ ; ; This is really just confirmation response, but good place to ensure session
274
+ ; ; selected
275
+ (" launch" (dap--switch-to-session debug-session))
276
+ (_
277
+ (unless dap-inhibit-io
278
+ (message " command: %s wasn't handled by dap-js. " command)))))
74
279
75
280
(provide 'dap-js )
76
281
; ;; dap-js.el ends here
0 commit comments