Skip to content

Commit

Permalink
Add option to open Dart DevTools via DAP session
Browse files Browse the repository at this point in the history
  • Loading branch information
ericdallo committed Apr 27, 2020
1 parent a27d122 commit 71bf099
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 9 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ You only need to run `dap-dart-setup` one time to setup the debugger.

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

* You can also open the [Dart DevTools](https://dart.dev/tools/dart-devtools) on the current debug session with `lsp-dart-dap-open-devtools`.

##### :warning:* Features only available for Dart SDK version 2.8.0 (currently the dev branch) or above.

## Supported settings
Expand All @@ -103,6 +105,8 @@ You only need to run `dap-dart-setup` one time to setup the debugger.
* `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)
* `lsp-dart-dap-debugger-path` - The debugger extension path.
* `lsp-dart-dap-debugger-program` - The command to execute the debugger extension.
* `lsp-dart-dap-devtools-theme` - The devtools theme when openning via `lsp-dart-dap-open-devtools`.
* `lsp-dart-dap-devtools-hide-options` - What to hide when openning DevTools via `lsp-dart-dap-open-devtools``. Defatuls to "debugger".

## Additional packages
* [lsp-ui](https://github.com/emacs-lsp/lsp-ui) : Flycheck, documentation and code actions support.
Expand Down
131 changes: 127 additions & 4 deletions lsp-dart-dap.el
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,30 @@
:group 'lsp-dart
:type '(repeat string))

(defcustom lsp-dart-dap-devtools-theme "dark"
"The theme to Dart DevTools."
:group 'lsp-dart
:type 'string)

(defcustom lsp-dart-dap-devtools-hide-options "debugger"
"What to hide when openning Dart DevTools."
:group 'lsp-dart
:type 'string)

(defconst lsp-dart-dap--devtools-buffer-name "*LSP Dart - DevTools*")
(defconst lsp-dart-dap--pub-list-pacakges-buffer-name "*LSP Dart - Pub list packages*")

(defun lsp-dart-dap--setup-extension ()
"Setup dart debugger extension to run `lsp-dart-dap-debugger-program`."
(message "DAP Dart :: Setting up...")
(lsp-dart-project-log "Setting up DAP...")
(lsp-async-start-process
(lambda ()
(lsp-async-start-process
(lambda () (message "DAP Dart :: Setup done!"))
(lambda () (message "DAP Dart :: Error setting up lsp-dart-dap, check if `npm` is on $PATH"))
(lambda () (lsp-dart-project-log "DAP setup done!"))
(lambda (_) (lsp-dart-project-log "Error setting up lsp-dart-dap, check if `npm` is on $PATH"))
(f-join lsp-dart-dap-debugger-path "extension/node_modules/typescript/bin/tsc")
"--project" (f-join lsp-dart-dap-debugger-path "extension")))
(lambda () (message "DAP Dart :: Error setting up lsp-dart-dap, check if `npm` is on $PATH"))
(lambda (_) (lsp-dart-project-log "Error setting up lsp-dart-dap, check if `npm` is on $PATH"))
"npm" "install" "--prefix" (f-join lsp-dart-dap-debugger-path "extension")
"--no-package-lock" "--silent" "--no-save"))

Expand Down Expand Up @@ -91,5 +104,115 @@
:program nil
:name "Dart::Run"))

(cl-defmethod dap-handle-event ((_event (eql dart.debuggerUris)) _session params)
"Handle debugger uris EVENT for SESSION with PARAMS."
(-let* (((&hash "vmServiceUri" vm-service-uri) params))
(lsp-workspace-set-metadata "dart-debug-vm-service-uri" vm-service-uri)))

(defun lsp-dart-dap--clean-buffer (buffer)
"Clean BUFFER content."
(when (get-buffer buffer)
(with-current-buffer buffer
(erase-buffer))))

(defun lsp-dart-dap--buffer-whole-string (buffer)
"Return all content of BUFFER."
(with-current-buffer buffer
(save-restriction
(widen)
(buffer-substring-no-properties (point-min) (point-max)))))

(defun lsp-dart-dap--check-devtools-uri (callback)
"Check for uri on devtools buffer and call CALLBACK with it.
If URI is not found on buffer, schedule re-check."
(let ((content (lsp-dart-dap--buffer-whole-string lsp-dart-dap--devtools-buffer-name)))
(if (string= content "")
(run-with-idle-timer 0.3 nil #'lsp-dart-dap--check-devtools-uri callback)
(-let* (((&hash "params" (&hash "host" "port")) (lsp--read-json content))
(uri (concat host ":" (number-to-string port))))
(lsp-workspace-set-metadata "dart-debug-devtools-uri" uri)
(funcall callback uri)))))

(defun lsp-dart-dap--devtools-activated-p ()
"Return non-nil if devtools is activated otherwise nil."
(lsp-dart-dap--clean-buffer lsp-dart-dap--pub-list-pacakges-buffer-name)
(let* ((pub (lsp-dart-project-get-pub-command))
(_proc (call-process pub
nil
lsp-dart-dap--pub-list-pacakges-buffer-name
nil
"global" "list"))
(content (lsp-dart-dap--buffer-whole-string lsp-dart-dap--pub-list-pacakges-buffer-name)))
(string-match-p "devtools \\([0-9]\\.[0-9]\\.[0-9]\\)" content)))

(defun lsp-dart-dap--activate-devtools (callback)
"Activate Dart Devtools via pub then call CALLBACK."
(lsp-dart-project-log "Activating DevTools...")
(let ((pub (lsp-dart-project-get-pub-command)))
(lsp-async-start-process
(lambda ()
(lsp-dart-project-log "DevTools activated successfully!")
(funcall callback))
(lambda (_) (lsp-dart-project-log "Could not Activate DevTools. \
Try to activate manually running 'pub global activate devtools'"))
pub "global" "activate" "devtools")))

(defun lsp-dart-dap--check-devtools-activated (callback)
"Check if devtools is activated otherwise prompt for activate it.
If it is already activated or after activated successfully, call CALLBACK."
(if (lsp-dart-dap--devtools-activated-p)
(funcall callback)
(when (y-or-n-p "Dart DevTools needs to be activated with \
'pub global activate devtools' to use this feature.\nActivate DevTools? ")
(lsp-dart-dap--activate-devtools callback))))

(defun lsp-dart-dap--kill-devtools-proc (proc _session)
"Kill the devtools PROC process of SESSION."
(lsp-workspace-set-metadata "dart-debug-devtools-uri" nil)
(delete-process proc)
(lsp-dart-dap--clean-buffer lsp-dart-dap--devtools-buffer-name))

(defvar-local lsp-dart-dap--check-devtools-uri-timer nil)

(defun lsp-dart-dap--start-devtools (callback)
"Start Dart DevTools process and call CALLBACK after started successfully."
(lsp-dart-dap--check-devtools-activated
(lambda ()
(if-let ((uri (lsp-workspace-get-metadata "dart-debug-devtools-uri")))
(funcall callback uri)
(let* ((pub (lsp-dart-project-get-pub-command))
(proc (start-process "Start DevTools"
lsp-dart-dap--devtools-buffer-name
pub "global" "run" "devtools"
"--machine"
"--enable-notifications"
"--try-ports" "10")))
(add-hook 'dap-terminated-hook (-partial #'lsp-dart-dap--kill-devtools-proc proc))
(when lsp-dart-dap--check-devtools-uri-timer
(cancel-timer lsp-dart-dap--check-devtools-uri-timer))
(setq lsp-dart-dap--check-devtools-uri-timer
(run-with-idle-timer 0.3 nil #'lsp-dart-dap--check-devtools-uri callback)))))))

(defun lsp-dart-dap--open-devtools (uri vm-service-uri)
"Open DevTools URI with VM-SERVICE-URI param at browser."
(let* ((params (url-build-query-string `((ide Emacs)
(uri ,vm-service-uri)
(hide ,lsp-dart-dap-devtools-hide-options)
(theme ,lsp-dart-dap-devtools-theme))))
(url (concat "http://" uri "?" params)))
(browse-url url)))

;;;###autoload
(defun lsp-dart-dap-open-devtools ()
"Open Dart DevTools for the current debug session."
(interactive)
(let ((session (dap--cur-session))
(vm-service-uri (lsp-workspace-get-metadata "dart-debug-vm-service-uri")))
(when (and session vm-service-uri)
(lsp-dart-dap--start-devtools
(lambda (uri)
(lsp-dart-project-log "Openning DevTools at browser...")
(lsp-dart-dap--open-devtools uri vm-service-uri))))))

(provide 'lsp-dart-dap)
;;; lsp-dart-dap.el ends here
11 changes: 11 additions & 0 deletions lsp-dart-project.el
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ flutter cache dir."
file-truename
(locate-dominating-file "bin")))))

(defun lsp-dart-project-get-pub-command ()
"Return the pub executable path from dart SDK path."
(-> (lsp-dart-project-get-sdk-dir)
file-name-as-directory
(concat "bin/pub")))

(defun lsp-dart-project-dart-command ()
"Return the dart executable from dart SDK dir."
(expand-file-name "bin/dart" (lsp-dart-project-get-sdk-dir)))
Expand All @@ -62,5 +68,10 @@ flutter cache dir."
"Return the dart or flutter project root."
(file-truename (locate-dominating-file default-directory "pubspec.yaml")))

(defun lsp-dart-project-log (msg &rest args)
"Log MSG with ARGS and custom prefix."
(let ((prefix (propertize "[LSP Dart]" 'face 'font-lock-keyword-face)))
(apply #'message (concat prefix " " msg) args)))

(provide 'lsp-dart-project)
;;; lsp-dart-project.el ends here
10 changes: 5 additions & 5 deletions lsp-dart.el
Original file line number Diff line number Diff line change
Expand Up @@ -371,11 +371,11 @@ If the version number could not be determined, signal an error."
(interactive)
(if (require 'pkg-info nil t)
(let ((version (pkg-info-version-info 'lsp-dart)))
(message "%s %s at %s @ Emacs %s"
(propertize "[LSP Dart]" 'face 'font-lock-keyword-face)
version
(format-time-string "%Y.%m.%d" (current-time))
emacs-version))
(lsp-dart-project-log
"%s at %s @ Emacs %s"
version
(format-time-string "%Y.%m.%d" (current-time))
emacs-version))
(error "Cannot determine version without package 'pkg-info'")))

;;;###autoload
Expand Down

0 comments on commit 71bf099

Please # to comment.