|
| 1 | +;;; lsp-dart-dap-devtools.el --- Support for Dart DevTools on debugger -*- lexical-binding: t; -*- |
| 2 | +;; |
| 3 | +;; Version: 1.8 |
| 4 | +;; Keywords: languages, extensions |
| 5 | +;; Package-Requires: ((emacs "25.2") (lsp-mode "6.0") (dap-mode "0.3") (dash "2.14.1")) |
| 6 | +;; URL: https://github.com/emacs-lsp/lsp-dart.el |
| 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 | +;;; Commentary: |
| 22 | +;; |
| 23 | +;; Support for Dart DevTools on debugger |
| 24 | +;; |
| 25 | +;;; Code: |
| 26 | + |
| 27 | +(require 'dash) |
| 28 | +(require 'lsp-mode) |
| 29 | +(require 'dap-mode) |
| 30 | + |
| 31 | +(require 'lsp-dart-project) |
| 32 | + |
| 33 | +(defcustom lsp-dart-dap-devtools-theme "dark" |
| 34 | + "The theme to Dart DevTools." |
| 35 | + :group 'lsp-dart |
| 36 | + :type 'string) |
| 37 | + |
| 38 | +(defcustom lsp-dart-dap-devtools-hide-options "debugger" |
| 39 | + "What to hide when openning Dart DevTools." |
| 40 | + :group 'lsp-dart |
| 41 | + :type 'string) |
| 42 | + |
| 43 | +(defconst lsp-dart-dap-devtools--buffer-name "*LSP Dart - DevTools*") |
| 44 | +(defconst lsp-dart-dap-devtools--pub-list-packages-buffer-name "*LSP Dart - Pub list packages*") |
| 45 | + |
| 46 | +(defun lsp-dart-dap-devtools-log (msg &rest args) |
| 47 | + "Custom logger for MSG and ARGS." |
| 48 | + (apply #'lsp-dart-project-custom-log "[DEVTOOLS]" msg args)) |
| 49 | + |
| 50 | +(cl-defmethod dap-handle-event ((_event (eql dart.debuggerUris)) _session params) |
| 51 | + "Handle debugger uris EVENT for SESSION with PARAMS." |
| 52 | + (-let* (((&hash "vmServiceUri" vm-service-uri) params)) |
| 53 | + (lsp-workspace-set-metadata "devtools-vm-service-uri" vm-service-uri))) |
| 54 | + |
| 55 | +(defun lsp-dart-dap-devtools--clean-buffer (buffer) |
| 56 | + "Clean BUFFER content." |
| 57 | + (when (get-buffer buffer) |
| 58 | + (with-current-buffer buffer |
| 59 | + (erase-buffer)))) |
| 60 | + |
| 61 | +(defun lsp-dart-dap-devtools--buffer-whole-string (buffer) |
| 62 | + "Return all content of BUFFER." |
| 63 | + (with-current-buffer buffer |
| 64 | + (save-restriction |
| 65 | + (widen) |
| 66 | + (buffer-substring-no-properties (point-min) (point-max))))) |
| 67 | + |
| 68 | +(defun lsp-dart-dap-devtools--check-devtools-uri (callback) |
| 69 | + "Check for uri on devtools buffer and call CALLBACK with it. |
| 70 | +If URI is not found on buffer, schedule re-check." |
| 71 | + (let ((content (lsp-dart-dap-devtools--buffer-whole-string lsp-dart-dap-devtools--buffer-name))) |
| 72 | + (if (string= content "") |
| 73 | + (run-with-idle-timer 0.3 nil #'lsp-dart-dap-devtools--check-devtools-uri callback) |
| 74 | + (-let* (((&hash "params" (&hash "host" "port")) (lsp--read-json content)) |
| 75 | + (uri (concat host ":" (number-to-string port)))) |
| 76 | + (lsp-workspace-set-metadata "dart-debug-devtools-uri" uri) |
| 77 | + (funcall callback uri))))) |
| 78 | + |
| 79 | +(defun lsp-dart-dap-devtools--activated-p () |
| 80 | + "Return non-nil if devtools is activated otherwise nil." |
| 81 | + (lsp-dart-dap-devtools--clean-buffer lsp-dart-dap-devtools--pub-list-packages-buffer-name) |
| 82 | + (let* ((pub (lsp-dart-project-get-pub-command)) |
| 83 | + (_proc (call-process pub |
| 84 | + nil |
| 85 | + lsp-dart-dap-devtools--pub-list-packages-buffer-name |
| 86 | + nil |
| 87 | + "global" "list")) |
| 88 | + (content (lsp-dart-dap-devtools--buffer-whole-string lsp-dart-dap-devtools--pub-list-packages-buffer-name))) |
| 89 | + (string-match-p "devtools \\([0-9]\\.[0-9]\\.[0-9]\\)" content))) |
| 90 | + |
| 91 | +(defun lsp-dart-dap-devtools--activate (callback) |
| 92 | + "Activate Dart Devtools via pub then call CALLBACK." |
| 93 | + (lsp-dart-dap-devtools-log "Activating...") |
| 94 | + (let ((pub (lsp-dart-project-get-pub-command))) |
| 95 | + (lsp-async-start-process |
| 96 | + (lambda () |
| 97 | + (lsp-dart-dap-devtools-log "Activated successfully!") |
| 98 | + (funcall callback)) |
| 99 | + (lambda (_) (lsp-dart-dap-devtools-log "Could not activate DevTools. \ |
| 100 | +Try to activate manually running 'pub global activate devtools'")) |
| 101 | + pub "global" "activate" "devtools"))) |
| 102 | + |
| 103 | +(defun lsp-dart-dap-devtools--check-activated (callback) |
| 104 | + "Check if devtools is activated otherwise prompt for activate it. |
| 105 | +If it is already activated or after activated successfully, call CALLBACK." |
| 106 | + (if (lsp-dart-dap-devtools--activated-p) |
| 107 | + (funcall callback) |
| 108 | + (when (y-or-n-p "Dart DevTools needs to be activated with \ |
| 109 | +'pub global activate devtools' to use this feature.\nActivate DevTools? ") |
| 110 | + (lsp-dart-dap-devtools--activate callback)))) |
| 111 | + |
| 112 | +(defun lsp-dart-dap-devtools--kill-proc (proc _session) |
| 113 | + "Kill the devtools PROC process of SESSION." |
| 114 | + (lsp-workspace-set-metadata "dart-debug-devtools-uri" nil) |
| 115 | + (delete-process proc) |
| 116 | + (lsp-dart-dap-devtools--clean-buffer lsp-dart-dap-devtools--buffer-name)) |
| 117 | + |
| 118 | +(defvar-local lsp-dart-dap-devtools--check-uri-timer nil) |
| 119 | + |
| 120 | +(defun lsp-dart-dap-devtools--start (callback) |
| 121 | + "Start Dart DevTools process and call CALLBACK after started successfully." |
| 122 | + (lsp-dart-dap-devtools--check-activated |
| 123 | + (lambda () |
| 124 | + (if-let ((uri (lsp-workspace-get-metadata "dart-debug-devtools-uri"))) |
| 125 | + (funcall callback uri) |
| 126 | + (let* ((pub (lsp-dart-project-get-pub-command)) |
| 127 | + (proc (start-process "Start DevTools" |
| 128 | + lsp-dart-dap-devtools--buffer-name |
| 129 | + pub "global" "run" "devtools" |
| 130 | + "--machine" |
| 131 | + "--enable-notifications" |
| 132 | + "--try-ports" "10"))) |
| 133 | + (add-hook 'dap-terminated-hook (-partial #'lsp-dart-dap-devtools--kill-proc proc)) |
| 134 | + (when lsp-dart-dap-devtools--check-uri-timer |
| 135 | + (cancel-timer lsp-dart-dap-devtools--check-uri-timer)) |
| 136 | + (setq lsp-dart-dap-devtools--check-uri-timer |
| 137 | + (run-with-idle-timer 0.3 nil #'lsp-dart-dap-devtools--check-devtools-uri callback))))))) |
| 138 | + |
| 139 | +(defun lsp-dart-dap-devtools--open (uri vm-service-uri) |
| 140 | + "Open DevTools URI with VM-SERVICE-URI param at browser." |
| 141 | + (let* ((params (url-build-query-string `((ide Emacs) |
| 142 | + (uri ,vm-service-uri) |
| 143 | + (hide ,lsp-dart-dap-devtools-hide-options) |
| 144 | + (theme ,lsp-dart-dap-devtools-theme)))) |
| 145 | + (url (concat "http://" uri "?" params))) |
| 146 | + (browse-url url))) |
| 147 | + |
| 148 | + |
| 149 | +;;; Public interface |
| 150 | + |
| 151 | +;;;###autoload |
| 152 | +(defun lsp-dart-dap-devtools-open () |
| 153 | + "Open Dart DevTools for the current debug session." |
| 154 | + (interactive) |
| 155 | + (let ((session (dap--cur-session)) |
| 156 | + (vm-service-uri (lsp-workspace-get-metadata "devtools-vm-service-uri"))) |
| 157 | + (when (and session vm-service-uri) |
| 158 | + (lsp-dart-dap-devtools--start |
| 159 | + (lambda (uri) |
| 160 | + (lsp-dart-dap-devtools-log "Openning at browser...") |
| 161 | + (lsp-dart-dap-devtools--open uri vm-service-uri)))))) |
| 162 | + |
| 163 | +(provide 'lsp-dart-dap-devtools) |
| 164 | +;;; lsp-dart-dap-devtools.el ends here |
0 commit comments