2020
2121; ;; Code:
2222
23+ (require 'cl-lib )
24+ (require 'dash )
25+ (require 'ht )
26+ (require 'json )
27+
2328(require 'dap-mode )
2429(require 'dap-utils )
2530
@@ -34,43 +39,243 @@ Link: https://marketplace.visualstudio.com/items?itemName=webfreak.debug ."
3439 :group 'dap-js
3540 :type '(repeat string))
3641
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 )
50108
51109(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)
64157
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 )
65162(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)))
66224
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)))))
74279
75280(provide 'dap-js )
76281; ;; dap-js.el ends here
0 commit comments