Skip to content

Commit 71bf099

Browse files
committed
Add option to open Dart DevTools via DAP session
1 parent a27d122 commit 71bf099

File tree

4 files changed

+147
-9
lines changed

4 files changed

+147
-9
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ You only need to run `dap-dart-setup` one time to setup the debugger.
8080

8181
![debug](https://raw.githubusercontent.com/emacs-lsp/lsp-dart/screenshots/debug.gif)
8282

83+
* You can also open the [Dart DevTools](https://dart.dev/tools/dart-devtools) on the current debug session with `lsp-dart-dap-open-devtools`.
84+
8385
##### :warning:* Features only available for Dart SDK version 2.8.0 (currently the dev branch) or above.
8486

8587
## Supported settings
@@ -103,6 +105,8 @@ You only need to run `dap-dart-setup` one time to setup the debugger.
103105
* `lsp-dart-dap-extension-version` - The debugger extension version. Defaults to [3.9.1](https://github.com/Dart-Code/Dart-Code/releases/tag/v3.9.1)
104106
* `lsp-dart-dap-debugger-path` - The debugger extension path.
105107
* `lsp-dart-dap-debugger-program` - The command to execute the debugger extension.
108+
* `lsp-dart-dap-devtools-theme` - The devtools theme when openning via `lsp-dart-dap-open-devtools`.
109+
* `lsp-dart-dap-devtools-hide-options` - What to hide when openning DevTools via `lsp-dart-dap-open-devtools``. Defatuls to "debugger".
106110

107111
## Additional packages
108112
* [lsp-ui](https://github.com/emacs-lsp/lsp-ui) : Flycheck, documentation and code actions support.

lsp-dart-dap.el

Lines changed: 127 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,30 @@
4848
:group 'lsp-dart
4949
:type '(repeat string))
5050

51+
(defcustom lsp-dart-dap-devtools-theme "dark"
52+
"The theme to Dart DevTools."
53+
:group 'lsp-dart
54+
:type 'string)
55+
56+
(defcustom lsp-dart-dap-devtools-hide-options "debugger"
57+
"What to hide when openning Dart DevTools."
58+
:group 'lsp-dart
59+
:type 'string)
60+
61+
(defconst lsp-dart-dap--devtools-buffer-name "*LSP Dart - DevTools*")
62+
(defconst lsp-dart-dap--pub-list-pacakges-buffer-name "*LSP Dart - Pub list packages*")
63+
5164
(defun lsp-dart-dap--setup-extension ()
5265
"Setup dart debugger extension to run `lsp-dart-dap-debugger-program`."
53-
(message "DAP Dart :: Setting up...")
66+
(lsp-dart-project-log "Setting up DAP...")
5467
(lsp-async-start-process
5568
(lambda ()
5669
(lsp-async-start-process
57-
(lambda () (message "DAP Dart :: Setup done!"))
58-
(lambda () (message "DAP Dart :: Error setting up lsp-dart-dap, check if `npm` is on $PATH"))
70+
(lambda () (lsp-dart-project-log "DAP setup done!"))
71+
(lambda (_) (lsp-dart-project-log "Error setting up lsp-dart-dap, check if `npm` is on $PATH"))
5972
(f-join lsp-dart-dap-debugger-path "extension/node_modules/typescript/bin/tsc")
6073
"--project" (f-join lsp-dart-dap-debugger-path "extension")))
61-
(lambda () (message "DAP Dart :: Error setting up lsp-dart-dap, check if `npm` is on $PATH"))
74+
(lambda (_) (lsp-dart-project-log "Error setting up lsp-dart-dap, check if `npm` is on $PATH"))
6275
"npm" "install" "--prefix" (f-join lsp-dart-dap-debugger-path "extension")
6376
"--no-package-lock" "--silent" "--no-save"))
6477

@@ -91,5 +104,115 @@
91104
:program nil
92105
:name "Dart::Run"))
93106

107+
(cl-defmethod dap-handle-event ((_event (eql dart.debuggerUris)) _session params)
108+
"Handle debugger uris EVENT for SESSION with PARAMS."
109+
(-let* (((&hash "vmServiceUri" vm-service-uri) params))
110+
(lsp-workspace-set-metadata "dart-debug-vm-service-uri" vm-service-uri)))
111+
112+
(defun lsp-dart-dap--clean-buffer (buffer)
113+
"Clean BUFFER content."
114+
(when (get-buffer buffer)
115+
(with-current-buffer buffer
116+
(erase-buffer))))
117+
118+
(defun lsp-dart-dap--buffer-whole-string (buffer)
119+
"Return all content of BUFFER."
120+
(with-current-buffer buffer
121+
(save-restriction
122+
(widen)
123+
(buffer-substring-no-properties (point-min) (point-max)))))
124+
125+
(defun lsp-dart-dap--check-devtools-uri (callback)
126+
"Check for uri on devtools buffer and call CALLBACK with it.
127+
If URI is not found on buffer, schedule re-check."
128+
(let ((content (lsp-dart-dap--buffer-whole-string lsp-dart-dap--devtools-buffer-name)))
129+
(if (string= content "")
130+
(run-with-idle-timer 0.3 nil #'lsp-dart-dap--check-devtools-uri callback)
131+
(-let* (((&hash "params" (&hash "host" "port")) (lsp--read-json content))
132+
(uri (concat host ":" (number-to-string port))))
133+
(lsp-workspace-set-metadata "dart-debug-devtools-uri" uri)
134+
(funcall callback uri)))))
135+
136+
(defun lsp-dart-dap--devtools-activated-p ()
137+
"Return non-nil if devtools is activated otherwise nil."
138+
(lsp-dart-dap--clean-buffer lsp-dart-dap--pub-list-pacakges-buffer-name)
139+
(let* ((pub (lsp-dart-project-get-pub-command))
140+
(_proc (call-process pub
141+
nil
142+
lsp-dart-dap--pub-list-pacakges-buffer-name
143+
nil
144+
"global" "list"))
145+
(content (lsp-dart-dap--buffer-whole-string lsp-dart-dap--pub-list-pacakges-buffer-name)))
146+
(string-match-p "devtools \\([0-9]\\.[0-9]\\.[0-9]\\)" content)))
147+
148+
(defun lsp-dart-dap--activate-devtools (callback)
149+
"Activate Dart Devtools via pub then call CALLBACK."
150+
(lsp-dart-project-log "Activating DevTools...")
151+
(let ((pub (lsp-dart-project-get-pub-command)))
152+
(lsp-async-start-process
153+
(lambda ()
154+
(lsp-dart-project-log "DevTools activated successfully!")
155+
(funcall callback))
156+
(lambda (_) (lsp-dart-project-log "Could not Activate DevTools. \
157+
Try to activate manually running 'pub global activate devtools'"))
158+
pub "global" "activate" "devtools")))
159+
160+
(defun lsp-dart-dap--check-devtools-activated (callback)
161+
"Check if devtools is activated otherwise prompt for activate it.
162+
If it is already activated or after activated successfully, call CALLBACK."
163+
(if (lsp-dart-dap--devtools-activated-p)
164+
(funcall callback)
165+
(when (y-or-n-p "Dart DevTools needs to be activated with \
166+
'pub global activate devtools' to use this feature.\nActivate DevTools? ")
167+
(lsp-dart-dap--activate-devtools callback))))
168+
169+
(defun lsp-dart-dap--kill-devtools-proc (proc _session)
170+
"Kill the devtools PROC process of SESSION."
171+
(lsp-workspace-set-metadata "dart-debug-devtools-uri" nil)
172+
(delete-process proc)
173+
(lsp-dart-dap--clean-buffer lsp-dart-dap--devtools-buffer-name))
174+
175+
(defvar-local lsp-dart-dap--check-devtools-uri-timer nil)
176+
177+
(defun lsp-dart-dap--start-devtools (callback)
178+
"Start Dart DevTools process and call CALLBACK after started successfully."
179+
(lsp-dart-dap--check-devtools-activated
180+
(lambda ()
181+
(if-let ((uri (lsp-workspace-get-metadata "dart-debug-devtools-uri")))
182+
(funcall callback uri)
183+
(let* ((pub (lsp-dart-project-get-pub-command))
184+
(proc (start-process "Start DevTools"
185+
lsp-dart-dap--devtools-buffer-name
186+
pub "global" "run" "devtools"
187+
"--machine"
188+
"--enable-notifications"
189+
"--try-ports" "10")))
190+
(add-hook 'dap-terminated-hook (-partial #'lsp-dart-dap--kill-devtools-proc proc))
191+
(when lsp-dart-dap--check-devtools-uri-timer
192+
(cancel-timer lsp-dart-dap--check-devtools-uri-timer))
193+
(setq lsp-dart-dap--check-devtools-uri-timer
194+
(run-with-idle-timer 0.3 nil #'lsp-dart-dap--check-devtools-uri callback)))))))
195+
196+
(defun lsp-dart-dap--open-devtools (uri vm-service-uri)
197+
"Open DevTools URI with VM-SERVICE-URI param at browser."
198+
(let* ((params (url-build-query-string `((ide Emacs)
199+
(uri ,vm-service-uri)
200+
(hide ,lsp-dart-dap-devtools-hide-options)
201+
(theme ,lsp-dart-dap-devtools-theme))))
202+
(url (concat "http://" uri "?" params)))
203+
(browse-url url)))
204+
205+
;;;###autoload
206+
(defun lsp-dart-dap-open-devtools ()
207+
"Open Dart DevTools for the current debug session."
208+
(interactive)
209+
(let ((session (dap--cur-session))
210+
(vm-service-uri (lsp-workspace-get-metadata "dart-debug-vm-service-uri")))
211+
(when (and session vm-service-uri)
212+
(lsp-dart-dap--start-devtools
213+
(lambda (uri)
214+
(lsp-dart-project-log "Openning DevTools at browser...")
215+
(lsp-dart-dap--open-devtools uri vm-service-uri))))))
216+
94217
(provide 'lsp-dart-dap)
95218
;;; lsp-dart-dap.el ends here

lsp-dart-project.el

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ flutter cache dir."
5454
file-truename
5555
(locate-dominating-file "bin")))))
5656

57+
(defun lsp-dart-project-get-pub-command ()
58+
"Return the pub executable path from dart SDK path."
59+
(-> (lsp-dart-project-get-sdk-dir)
60+
file-name-as-directory
61+
(concat "bin/pub")))
62+
5763
(defun lsp-dart-project-dart-command ()
5864
"Return the dart executable from dart SDK dir."
5965
(expand-file-name "bin/dart" (lsp-dart-project-get-sdk-dir)))
@@ -62,5 +68,10 @@ flutter cache dir."
6268
"Return the dart or flutter project root."
6369
(file-truename (locate-dominating-file default-directory "pubspec.yaml")))
6470

71+
(defun lsp-dart-project-log (msg &rest args)
72+
"Log MSG with ARGS and custom prefix."
73+
(let ((prefix (propertize "[LSP Dart]" 'face 'font-lock-keyword-face)))
74+
(apply #'message (concat prefix " " msg) args)))
75+
6576
(provide 'lsp-dart-project)
6677
;;; lsp-dart-project.el ends here

lsp-dart.el

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,11 @@ If the version number could not be determined, signal an error."
371371
(interactive)
372372
(if (require 'pkg-info nil t)
373373
(let ((version (pkg-info-version-info 'lsp-dart)))
374-
(message "%s %s at %s @ Emacs %s"
375-
(propertize "[LSP Dart]" 'face 'font-lock-keyword-face)
376-
version
377-
(format-time-string "%Y.%m.%d" (current-time))
378-
emacs-version))
374+
(lsp-dart-project-log
375+
"%s at %s @ Emacs %s"
376+
version
377+
(format-time-string "%Y.%m.%d" (current-time))
378+
emacs-version))
379379
(error "Cannot determine version without package 'pkg-info'")))
380380

381381
;;;###autoload

0 commit comments

Comments
 (0)