Skip to content

Commit 7fcc204

Browse files
committed
Finish support for choose an emulator
1 parent 807f82d commit 7fcc204

File tree

2 files changed

+138
-62
lines changed

2 files changed

+138
-62
lines changed

lsp-dart-dap.el

Lines changed: 63 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -145,37 +145,53 @@ Required to support 'Inspect Widget'."
145145

146146
;; Flutter
147147

148-
(defun lsp-dart-dap--flutter-get-or-create-device ()
149-
"Return the device to debug or prompt to start it."
150-
(-let* ((devices (lsp-dart-flutter-daemon-get-emulators))
151-
(chosen-device (dap--completing-read "Select a device to use: "
152-
devices
153-
(-lambda ((&hash "id" "name" "category" "platformType" platform))
154-
(format "%s - %s" platform (if name name id)))
155-
nil
156-
t))
157-
(emulator (lsp-dart-flutter-daemon-launch chosen-device)))
158-
(ht ('id "emulator-5554")
159-
('name "device"))
160-
))
148+
(declare-function all-the-icons-faicon "ext:all-the-icons")
149+
150+
(defun lsp-dart-dap--device-label (id name platform)
151+
"Return a friendly label for device with ID, NAME and PLATFORM.
152+
Check for icons if supports it."
153+
(let* ((device-name (if name name id))
154+
(default (concat platform " - " device-name)))
155+
(if (featurep 'all-the-icons)
156+
(pcase platform
157+
("android" (concat (all-the-icons-faicon "android" :face 'all-the-icons-green) " " device-name))
158+
("ios" (concat (all-the-icons-faicon "apple" :face 'all-the-icons-lsilver) " " device-name))
159+
(_ default))
160+
default)))
161+
162+
(defun lsp-dart-dap--flutter-get-or-create-device (callback)
163+
"Return the device to debug or prompt to start it.
164+
Call CALLBACK when the device is chosen and started successfully."
165+
(lsp-dart-flutter-daemon-get-emulators
166+
(lambda (devices)
167+
(-let* ((chosen-device (dap--completing-read "Select a device to use: "
168+
devices
169+
(-lambda ((&hash "id" "name" "platformType" platform))
170+
(lsp-dart-dap--device-label id name platform))
171+
nil
172+
t)))
173+
(lsp-dart-flutter-daemon-launch chosen-device callback)))))
161174

162175
(defun lsp-dart-dap--populate-flutter-start-file-args (conf)
163176
"Populate CONF with the required arguments for Flutter debug."
164-
(-let* ((root (lsp-dart-project-get-root))
165-
((&hash "id" device-id "name" device-name) (lsp-dart-dap--flutter-get-or-create-device)))
166-
(-> conf
167-
(dap--put-if-absent :dap-server-path lsp-dart-dap-flutter-debugger-program)
168-
(dap--put-if-absent :cwd root)
169-
(dap--put-if-absent :program (lsp-dart-project-get-entrypoint))
170-
(dap--put-if-absent :dartPath (lsp-dart-project-dart-command))
171-
(dap--put-if-absent :flutterPath (lsp-dart-project-get-flutter-path))
172-
(dap--put-if-absent :flutterTrackWidgetCreation lsp-dart-dap-flutter-track-widget-creation)
173-
(dap--put-if-absent :useFlutterStructuredErrors lsp-dart-dap-flutter-structured-errors)
174-
(dap--put-if-absent :debugExternalLibraries lsp-dart-dap-debug-external-libraries)
175-
(dap--put-if-absent :debugSdkLibraries lsp-dart-dap-debug-sdk-libraries)
176-
(dap--put-if-absent :deviceId device-id)
177-
(dap--put-if-absent :deviceName device-name)
178-
(dap--put-if-absent :name (concat "Flutter (" device-name ")")))))
177+
(let ((pre-conf (-> conf
178+
(dap--put-if-absent :dap-server-path lsp-dart-dap-flutter-debugger-program)
179+
(dap--put-if-absent :cwd (lsp-dart-project-get-root))
180+
(dap--put-if-absent :program (lsp-dart-project-get-entrypoint))
181+
(dap--put-if-absent :dartPath (lsp-dart-project-dart-command))
182+
(dap--put-if-absent :flutterPath (lsp-dart-project-get-flutter-path))
183+
(dap--put-if-absent :flutterTrackWidgetCreation lsp-dart-dap-flutter-track-widget-creation)
184+
(dap--put-if-absent :useFlutterStructuredErrors lsp-dart-dap-flutter-structured-errors)
185+
(dap--put-if-absent :debugExternalLibraries lsp-dart-dap-debug-external-libraries)
186+
(dap--put-if-absent :debugSdkLibraries lsp-dart-dap-debug-sdk-libraries))))
187+
(lambda (start-debugging-callback)
188+
(lsp-dart-dap--flutter-get-or-create-device
189+
(-lambda ((&hash "id" device-id "name" device-name))
190+
(funcall start-debugging-callback
191+
(-> pre-conf
192+
(dap--put-if-absent :deviceId device-id)
193+
(dap--put-if-absent :deviceName device-name)
194+
(dap--put-if-absent :name (concat "Flutter (" device-name ")")))))))))
179195

180196
(dap-register-debug-provider "flutter" 'lsp-dart-dap--populate-flutter-start-file-args)
181197
(dap-register-debug-template "Flutter :: Debug"
@@ -186,6 +202,21 @@ Required to support 'Inspect Widget'."
186202
:name "Flutter"))
187203

188204
(defvar lsp-dart-dap--flutter-progress-reporter nil)
205+
(defvar lsp-dart-dap--flutter-progress-reporter-timer nil)
206+
207+
(defun lsp-dart-dap--flutter-progress-timer-cancel (_debug-session)
208+
"Cancel the Flutter progress timer for DEBUG-SESSION."
209+
(setq lsp-dart-dap--flutter-progress-reporter nil)
210+
(setq lsp-dart-dap--flutter-progress-reporter-timer nil)
211+
(when lsp-dart-dap--flutter-progress-reporter-timer
212+
(cancel-timer lsp-dart-dap--flutter-progress-reporter-timer))
213+
214+
(add-hook 'dap-terminated-hook #'lsp-dart-dap--flutter-progress-timer-cancel)
215+
216+
(defun lsp-dart-dap--flutter-progress-update ()
217+
"Update the flutter progress reporter."
218+
(when lsp-dart-dap--flutter-progress-reporter
219+
(progress-reporter-update lsp-dart-dap--flutter-progress-reporter))))
189220

190221
(cl-defmethod dap-handle-event ((_event (eql dart.log)) _session params)
191222
"Handle debugger uris EVENT for SESSION with PARAMS."
@@ -201,7 +232,9 @@ Required to support 'Inspect Widget'."
201232
(propertize "[DAP] "
202233
'face 'font-lock-function-name-face))))
203234
(setq lsp-dart-dap--flutter-progress-reporter
204-
(make-progress-reporter (concat prefix (gethash "message" params))))))
235+
(make-progress-reporter (concat prefix (gethash "message" params))))
236+
(setq lsp-dart-dap--flutter-progress-reporter-timer
237+
(run-with-timer 0.2 0.2 #'lsp-dart-dap--flutter-progress-update))))
205238

206239
(cl-defmethod dap-handle-event ((_event (eql dart.launched)) _session _params)
207240
"Handle debugger uris EVENT for SESSION with PARAMS."
@@ -221,7 +254,7 @@ Required to support 'Inspect Widget'."
221254

222255
(cl-defmethod dap-handle-event ((_event (eql dart.flutter.firstFrame)) _session _params)
223256
"Handle debugger uris EVENT for SESSION with PARAMS."
224-
(setq lsp-dart-dap--flutter-progress-reporter nil)
257+
(lsp-dart-dap--flutter-progress-timer-cancel (dap--cur-session))
225258
(lsp-dart-dap-log "App ready!"))
226259

227260
(cl-defmethod dap-handle-event ((_event (eql dart.serviceRegistered)) _session _params)

lsp-dart-flutter-daemon.el

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
;;
33
;; Version: 1.8
44
;; Keywords: languages, extensions
5-
;; Package-Requires: ((emacs "25.2") (lsp-mode "6.0"))
5+
;; Package-Requires: ((emacs "25.2") (lsp-mode "6.0") (dash "2.14.1"))
66
;; URL: https://github.com/emacs-lsp/lsp-dart.el
77
;;
88
;; This program is free software; you can redistribute it and/or modify
@@ -25,13 +25,26 @@
2525
;;; Code:
2626

2727
(require 'comint)
28+
(require 'dash)
29+
(require 'ht)
2830
(require 'lsp-mode)
2931

3032
(require 'lsp-dart-project)
3133

3234
(defconst lsp-dart-flutter-daemon-buffer-name "*LSP Dart - Flutter daemon*")
3335
(defconst lsp-dart-flutter-daemon-name "LSP Dart - Flutter daemon")
3436

37+
(defvar lsp-dart-flutter-daemon-current-command nil)
38+
(defvar lsp-dart-flutter-daemon-current-device nil)
39+
40+
(defun lsp-dart-flutter-daemon-log (level msg &rest args)
41+
"Log for LEVEL, MSG with ARGS adding lsp-dart-flutter-daemon prefix."
42+
(lsp-dart-project-log (concat
43+
(propertize (concat "[FLUTTER " (upcase level) "] ")
44+
'face 'font-lock-function-name-face)
45+
msg
46+
args)))
47+
3548
(defun lsp-dart-flutter-daemon--generate-command-id ()
3649
"Generate a random command id."
3750
(random 10000))
@@ -57,50 +70,80 @@ PARAMS is the optional method params."
5770
(lsp--json-serialize command)
5871
"]\n")))
5972

60-
(defvar lsp-dart-flutter-daemon-response nil)
61-
62-
(defun lsp-dart-flutter-daemon-handle-response (id event-name response)
63-
"Handle RESPONSE and save if it is for ID.
64-
Wait for next response if EVENT-NAME is non null."
65-
(when (string-prefix-p "[" response)
66-
(-let* (((&hash "id" resp-id "result" "event" "params") (lsp--read-json (substring response 1 -2))))
67-
(if event-name
68-
(when (string= event event-name)
69-
(setq lsp-dart-flutter-daemon-response params))
73+
(defun lsp-dart-flutter-daemon-raw->response (response)
74+
"Parse raw RESPONSE into a list of responses."
75+
(when (string-prefix-p "[" (string-trim response))
76+
(--> response
77+
string-trim
78+
(replace-regexp-in-string (regexp-quote "\n") "" it nil 'literal)
79+
(replace-regexp-in-string (regexp-quote "][") "]\n[" it nil 'literal)
80+
(split-string it "\n")
81+
(-map (lambda (el) (seq-first (lsp--read-json el))) it))))
82+
83+
(defun lsp-dart-flutter-daemon-handle-events (raw-response)
84+
"Handle Flutter daemon events from RAW-RESPONSE."
85+
(-map (-lambda ((&hash "event" "params" (params &as &hash? "level" "message")))
86+
(when event
87+
(pcase event
88+
("device.removed" (setq lsp-dart-flutter-daemon-current-device nil))
89+
90+
("device.added" (setq lsp-dart-flutter-daemon-current-device params))
91+
92+
("daemon.logMessage" (lsp-dart-flutter-daemon-log level message)))))
93+
(lsp-dart-flutter-daemon-raw->response raw-response)))
94+
95+
(defun lsp-dart-flutter-daemon-handle-response (raw-response)
96+
"Handle the RAW-RESPONSE from comint output."
97+
(when lsp-dart-flutter-daemon-current-command
98+
(--map
99+
(-let* (((&hash "id" resp-id "result" "event" "params") it)
100+
((&hash "id" "callback" "event-name") lsp-dart-flutter-daemon-current-command))
101+
(if event-name
102+
(when (string= event event-name)
103+
(remove-hook 'comint-output-filter-functions #'lsp-dart-flutter-daemon-handle-response)
104+
(funcall callback params))
70105
(when (= resp-id id)
71-
(setq lsp-dart-flutter-daemon-response result))))))
106+
(remove-hook 'comint-output-filter-functions #'lsp-dart-flutter-daemon-handle-response)
107+
(if result
108+
(funcall callback result)
109+
(funcall callback)))))
110+
(lsp-dart-flutter-daemon-raw->response raw-response))))
72111

73112
(defun lsp-dart-flutter-daemon-running-p ()
74113
"Return non-nil if the Flutter daemon is already running."
75114
(comint-check-proc lsp-dart-flutter-daemon-buffer-name))
76115

77-
(defun lsp-dart-flutter-daemon--send (method &optional params event-name)
78-
"Send a command with METHOD to a Flutter daemon and await for a response.
116+
(defun lsp-dart-flutter-daemon--send (method callback &optional params event-name)
117+
"Send a command with METHOD to the daemon and call CALLBACK with the response.
79118
PARAMS is the optional method args and should be a hash-table.
80119
If EVENT-NAME is non-nil, it will this event to return its value.
81120
Starts the daemon if is not running yet."
82121
(unless (lsp-dart-flutter-daemon-running-p)
83122
(lsp-dart-flutter-daemon--start))
84-
(setq lsp-dart-flutter-daemon-response nil)
85123
(let* ((id (lsp-dart-flutter-daemon--generate-command-id))
86124
(command (lsp-dart-flutter-daemon--build-command id method params)))
87-
(remove-hook 'comint-output-filter-functions (-partial #'lsp-dart-flutter-daemon-handle-response id event-name) t)
88-
(add-hook 'comint-output-filter-functions (-partial #'lsp-dart-flutter-daemon-handle-response id event-name))
89-
(comint-send-string (get-buffer-process lsp-dart-flutter-daemon-buffer-name) command)
90-
(while (not lsp-dart-flutter-daemon-response)
91-
(sit-for 0.1))
92-
lsp-dart-flutter-daemon-response))
93-
94-
(defun lsp-dart-flutter-daemon-get-emulators ()
95-
"Return the available emulators from Flutter daemon."
96-
(lsp-dart-flutter-daemon--send "emulator.getEmulators"))
97-
98-
(defun lsp-dart-flutter-daemon-launch (device)
99-
"Launch emulator for DEVICE and wait for connected state."
100-
(-let* (((&hash "id") device)
101-
(params (ht ('emulatorId id))))
102-
(lsp-dart-flutter-daemon--send "device.enable")
103-
(lsp-dart-flutter-daemon--send "emulator.launch" params "device.added")))
125+
(setq lsp-dart-flutter-daemon-current-command (ht ("id" id)
126+
("callback" callback)
127+
("event-name" event-name)))
128+
(add-hook 'comint-output-filter-functions #'lsp-dart-flutter-daemon-handle-response)
129+
(comint-send-string (get-buffer-process lsp-dart-flutter-daemon-buffer-name) command)))
130+
131+
(defun lsp-dart-flutter-daemon-get-emulators (callback)
132+
"Call CALLBACK with the available emulators from Flutter daemon."
133+
(lsp-dart-flutter-daemon--send "emulator.getEmulators" callback))
134+
135+
(defun lsp-dart-flutter-daemon-launch (device callback)
136+
"Launch emulator for DEVICE and wait for connected state and call CALLBACK."
137+
(if lsp-dart-flutter-daemon-current-device
138+
(funcall callback lsp-dart-flutter-daemon-current-device)
139+
(-let* (((&hash "id") device)
140+
(params (ht ("emulatorId" id))))
141+
(lsp-dart-flutter-daemon--send
142+
"device.enable"
143+
(lambda ()
144+
(remove-hook 'comint-output-filter-functions #'lsp-dart-flutter-daemon-handle-events)
145+
(add-hook 'comint-output-filter-functions #'lsp-dart-flutter-daemon-handle-events)
146+
(lsp-dart-flutter-daemon--send "emulator.launch" callback params "device.added"))))))
104147

105148
;;;###autoload
106149
(define-derived-mode lsp-dart-flutter-daemon-mode comint-mode lsp-dart-flutter-daemon-name

0 commit comments

Comments
 (0)