forked from emacs-lsp/dap-mode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdap-ui-repl.el
149 lines (132 loc) · 5.81 KB
/
dap-ui-repl.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
;;; dap-ui-repl.el --- DAP REPL -*- lexical-binding: t; -*-
;; This is free and unencumbered software released into the public domain.
;;; Commentary:
;; This code is heavily based on `skewer-repl'. Run `dap-ui-repl' to switch to
;; the REPL buffer and evaluate code.
;;; Code:
(require 'comint)
(require 'compile)
(require 'dash)
(require 'dap-mode)
(require 'lsp-mode)
(defcustom dap-ui-repl-prompt ">> "
"Prompt string for DAP REPL."
:type 'string
:group 'dap-ui)
(defvar dap-ui-repl-welcome
(propertize "*** Welcome to Dap-Ui ***\n"
'font-lock-face 'font-lock-comment-face)
"Header line to show at the top of the REPL buffer.
Hack notice: this allows log messages to appear before anything is
evaluated because it provides insertable space at the top of the
buffer.")
(defun dap-ui-repl-process ()
"Return the process for the dap-ui REPL."
(get-buffer-process (current-buffer)))
(define-derived-mode dap-ui-repl-mode comint-mode "DAP-REPL"
"Provide a REPL for the active debug session."
:group 'dap-ui
:syntax-table emacs-lisp-mode-syntax-table
(setq comint-prompt-regexp (concat "^" (regexp-quote dap-ui-repl-prompt))
comint-input-sender 'dap-ui-input-sender
comint-process-echoes nil)
;; Make opportunistic use of company-mode, but don't require it.
;; This means company-backends may be undeclared, so don't emit a
;; warning about it.
(with-no-warnings
(setq-local company-backends '(company-dap-ui-repl)))
(unless (comint-check-proc (current-buffer))
(insert dap-ui-repl-welcome)
(start-process "dap-ui-repl" (current-buffer) nil)
(set-process-query-on-exit-flag (dap-ui-repl-process) nil)
(goto-char (point-max))
(set (make-local-variable 'comint-inhibit-carriage-motion) t)
(comint-output-filter (dap-ui-repl-process) dap-ui-repl-prompt)
(set-process-filter (dap-ui-repl-process) 'comint-output-filter)))
(defun dap-ui-input-sender (_ input)
"REPL comint handler.
INPUT is the current input."
(let ((debug-session (dap--cur-active-session-or-die)))
(if-let ((active-frame-id (-some->> debug-session
dap--debug-session-active-frame
(gethash "id"))))
(dap--send-message
(dap--make-request "evaluate"
(list :expression input
:frameId active-frame-id))
(-lambda ((&hash "success" "message" "body"))
(-when-let (buffer (get-buffer "*dap-ui-repl*"))
(with-current-buffer buffer
(comint-output-filter (dap-ui-repl-process)
(concat (if success (gethash "result" body) message)
"\n"
dap-ui-repl-prompt)))))
debug-session)
(error "There is no stopped debug session"))))
;;;###autoload
(defun dap-ui-repl ()
"Start a JavaScript REPL to be evaluated in the visiting browser."
(interactive)
(let ((workspaces (lsp-workspaces)))
(unless (get-buffer "*dap-ui-repl*")
(with-current-buffer (get-buffer-create "*dap-ui-repl*")
(dap-ui-repl-mode)
(when (functionp 'company-mode)
(company-mode 1))
(setq-local lsp--buffer-workspaces workspaces))))
(pop-to-buffer (get-buffer "*dap-ui-repl*")))
(defun dap-ui-repl--calculate-candidates ()
"Calculate candidates.
TEXT is the current input."
(let ((text (comint-get-old-input-default))
(debug-session (dap--cur-active-session-or-die)))
(if-let (frame-id (-some->> debug-session
dap--debug-session-active-frame
(gethash "id")))
(cons :async
(lambda (callback)
(dap--send-message
(dap--make-request "completions"
(list :frameId frame-id
:text text
:column (- (length text) (- (point-at-eol) (point)))))
(dap--resp-handler
(lambda (result)
(-if-let (targets (-some->> result (gethash "body") (gethash "targets")))
(funcall callback (-map (-lambda ((item &as &hash "label" "text" "type"))
(propertize label :text text :type type :dap-completion-item item))
targets))
(funcall callback ()))))
debug-session))))))
(defun dap-ui-repl--post-completion (candidate)
"Post completion handling for CANDIDATE."
(let ((to-insert (plist-get (text-properties-at 0 candidate) :text)))
(when to-insert
(delete-char (- (length candidate)))
(insert to-insert))))
(defun dap-ui-repl--annotate (candidate)
"Get annotation for CANDIDATE."
(concat " " (plist-get (text-properties-at 0 candidate) :type)))
(defun company-dap-ui-repl (command &optional candidate &rest _args)
"Dap-Ui REPL backend for company-mode.
See `company-backends' for more info about COMMAND and CANDIDATE."
(interactive (list 'interactive))
(cl-case command
(interactive
(with-no-warnings ;; opportunistic use of company-mode
(company-begin-backend 'company-dap-ui-repl)))
(prefix (dap-ui-repl-company-prefix))
(ignore-case t)
(sorted t)
(match (length candidate))
(annotation (dap-ui-repl--annotate candidate))
(candidates (dap-ui-repl--calculate-candidates))
(post-completion (dap-ui-repl--post-completion candidate))))
(defun dap-ui-repl-company-prefix ()
"Prefix for company."
(and (eq major-mode 'dap-ui-repl-mode)
(or (with-no-warnings ;; opportunistic use of company-mode
(company-grab-word))
'stop)))
(provide 'dap-ui-repl)
;;; dap-ui-repl.el ends here