@@ -91,6 +91,210 @@ The input are the file name and the major mode of the buffer."
91
91
:path " copilot-language-server"
92
92
:version lsp-copilot-version))
93
93
94
+
95
+ ; ;; Panel Completion
96
+
97
+ (defvar-local lsp-copilot-panel-completion-items nil
98
+ " A list of completion items returned by the Panel Completion call" )
99
+
100
+ (defvar-local lsp-copilot-panel-completion-token nil
101
+ " The per-request token" )
102
+
103
+ (defvar-local lsp-copilot-panel-modification-tick nil
104
+ " Modification tick when the panel was called" )
105
+
106
+ (defun lsp-copilot--panel-accept-item (completing-buffer-name original-tick item )
107
+ (when (buffer-live-p (get-buffer completing-buffer-name))
108
+ (with-current-buffer completing-buffer-name
109
+ (unless (= original-tick (buffer-chars-modified-tick ))
110
+ (user-error " Can not accept the suggestion on a modified buffer. Try copying it" ))
111
+
112
+ (-let* (((&copilot-ls:PanelCompletionItem? :insert-text :range? :command? ) item)
113
+ ((start . end) (when range?
114
+ (-let (((&RangeToPoint :start :end ) range?) ) (cons start end)))))
115
+
116
+ (unless (and start end)
117
+ (error " Server did not provide a range -- Can not insert " ))
118
+
119
+ (lsp-with-undo-amalgamate
120
+ (delete-region start end)
121
+ (goto-char start)
122
+ (insert insert-text))
123
+
124
+ ; ; Post command
125
+ (when command?
126
+ (lsp--execute-command command?) )))))
127
+
128
+ (defun lsp-copilot--panel-accept-suggestion-at-point ()
129
+ (interactive )
130
+ (let ((completing-buffer-name (get-text-property (point ) 'lsp-panel-completing-buffer-name ))
131
+ (original-tick (get-text-property (point ) 'lsp-original-tick ))
132
+ (item (get-text-property (point ) 'lsp-panel-item )))
133
+ (lsp-copilot--panel-accept-item completing-buffer-name original-tick item)
134
+ (quit-window )))
135
+
136
+ (defun lsp-copilot--panel-accept-button-click (b )
137
+ (when-let* ((item (overlay-get b 'lsp-panel-item ))
138
+ (completing-buffer-name (overlay-get b 'lsp-panel-completing-buffer-name ))
139
+ (original-tick (overlay-get b 'lsp-original-tick )))
140
+ (lsp-copilot--panel-accept-item completing-buffer-name original-tick item)
141
+
142
+ (quit-window )))
143
+
144
+ (defun lsp-copilot--panel-copy-button-click (b )
145
+
146
+ (kill-new (lsp:copilot-ls-panel-completion-item-insert-text (overlay-get b 'lsp-panel-item )))
147
+ (quit-window ))
148
+
149
+ (defun lsp-copilot--panel-forward-suggestion (arg )
150
+ (interactive " p" )
151
+ (org-forward-heading-same-level arg)
152
+ (recenter-top-bottom 0 )
153
+ (org-down-element ))
154
+
155
+ (defun lsp-copilot--panel-backward-suggestion (arg )
156
+ (interactive " p" )
157
+ (org-backward-heading-same-level arg)
158
+ (recenter-top-bottom 0 )
159
+ (org-down-element ))
160
+
161
+ (define-derived-mode lsp-copilot-panel-buffer-mode org-mode " CopilotPanel"
162
+ " A major mode for completions "
163
+ :group 'lsp-copilot )
164
+
165
+
166
+ (let ((key-bindings '((" C-n" . lsp-copilot--panel-forward-suggestion)
167
+ (" C-p" . lsp-copilot--panel-backward-suggestion)
168
+ (" C-<return>" . lsp-copilot--panel-accept-suggestion-at-point)
169
+ (" q" . quit-window))))
170
+ (dolist (binding key-bindings)
171
+ (bind-key (kbd (car binding)) (cdr binding) 'lsp-copilot-panel-buffer-mode-map )))
172
+
173
+
174
+ (defcustom lsp-copilot-panel-display-fn #'pop-to-buffer
175
+ " Function used to display the panel completions buffer"
176
+ :type 'function
177
+ :group 'lsp-copilot )
178
+
179
+
180
+ (defun lsp-copilot--panel-display-buffer (completing-buffer-name items original-tick )
181
+ " Builds and display the panel buffer"
182
+ (if (lsp-inline-completion--active-p)
183
+ (progn
184
+ (lsp-inline-completion-cancel)))
185
+
186
+ (when (and (bound-and-true-p company-mode)
187
+ (company--active-p))
188
+ (company-cancel))
189
+
190
+ (let ((buf (get-buffer-create (format " *lsp-copilot-panel-results-%s * " completing-buffer-name) )))
191
+ (with-current-buffer buf
192
+ (read-only-mode -1 )
193
+ (erase-buffer )
194
+ (fundamental-mode )
195
+
196
+ ; ; (insert "#+STARTUP: noindent\n\n")
197
+ (setq-local org-startup-indented nil )
198
+ (cl-loop for item in items
199
+ for i from 1
200
+ do
201
+ (-let* ((start-point (point ))
202
+ ((&copilot-ls:PanelCompletionItem? :insert-text ) item)
203
+ (heading (format " * Solution %d " i))
204
+ (src (format " #+begin_src %s \n %s \n #+end_src\n " " python" insert-text)))
205
+
206
+ (insert heading)
207
+ (insert ?\n ?\n )
208
+ (insert-button " Accept"
209
+ 'action #'lsp-copilot--panel-accept-button-click
210
+ 'lsp-original-tick original-tick
211
+ 'lsp-panel-item item
212
+ 'lsp-panel-completing-buffer-name completing-buffer-name)
213
+
214
+ (insert ?\s )
215
+
216
+ (insert-button " Copy"
217
+ 'action #'lsp-copilot--panel-copy-button-click
218
+ 'lsp-original-tick original-tick
219
+ 'lsp-panel-item item
220
+ 'lsp-panel-completing-buffer-name completing-buffer-name)
221
+
222
+ (insert ?\n )
223
+ (insert src)
224
+ (insert ?\n ?\n )
225
+
226
+ (put-text-property start-point (point ) 'lsp-original-tick original-tick)
227
+ (put-text-property start-point (point ) 'lsp-panel-item item)
228
+ (put-text-property start-point (point ) 'lsp-panel-completing-buffer-name completing-buffer-name)))
229
+
230
+ (delete-all-space t )
231
+ (lsp-copilot-panel-buffer-mode)
232
+ (read-only-mode +1 )
233
+
234
+ (goto-char (point-min ))
235
+ (org-down-element ))
236
+
237
+ (if (get-buffer-window completing-buffer-name 'visible )
238
+ (progn
239
+ (select-window (get-buffer-window completing-buffer-name 'visible ))
240
+ (funcall lsp-copilot-panel-display-fn buf))
241
+ (user-error " The original buffer for completions was not active; not showing panel" ))))
242
+
243
+
244
+ (defun lsp-copilot-panel-display-buffer ()
245
+ " Displays a completion panel with the items collected by the last call of lsp-copilot-panel-completion"
246
+ (interactive )
247
+
248
+ (if lsp-copilot-panel-completion-items
249
+ (lsp-copilot--panel-display-buffer (buffer-name ) lsp-copilot-panel-completion-items lsp-copilot-panel-modification-tick)
250
+ (lsp--error " No completions to display" )))
251
+
252
+ (defun lsp-copilot--panel-completions-progress-handler (workspace params )
253
+ (-let* (((&ProgressParams :token :value ) params)
254
+ ((action completing-buffer-name panel-completion-token) (string-split token " /// " )))
255
+ (pcase action
256
+ ; ; copilot sends results in the report
257
+ (" PANEL-PARTIAL-RESULT"
258
+ (when (and (lsp-copilot-ls-panel-completion-items? value)
259
+ (buffer-live-p (get-buffer completing-buffer-name)))
260
+ (with-current-buffer completing-buffer-name
261
+ (when (string-equal panel-completion-token lsp-copilot-panel-completion-token)
262
+ (setq-local lsp-copilot-panel-completion-items
263
+ (nconc lsp-copilot-panel-completion-items
264
+ (seq-into (lsp:copilot-ls-panel-completion-items-items value) 'list )))))))
265
+ (" PANEL-WORK-DONE"
266
+ (when (and (lsp-work-done-progress-end? value)
267
+ (buffer-live-p (get-buffer completing-buffer-name))
268
+ (string-equal (lsp:work-done-progress-end-kind value) " end" ))
269
+ (with-current-buffer completing-buffer-name
270
+ (when (string-equal panel-completion-token lsp-copilot-panel-completion-token)
271
+ (lsp-copilot-panel-display-buffer))))))))
272
+
273
+ (defun lsp-copilot-panel-completion ()
274
+ " Use a Completion Panel to provide suggestions at point"
275
+ (interactive )
276
+
277
+ (lsp-inline-completion-inhibit-idle-completions)
278
+ (lsp-inline-completion-cancel-timer)
279
+
280
+ (let* ((token (uuidgen-1))
281
+ (partial-token (format " PANEL-PARTIAL-RESULT /// %s /// %s " (buffer-name ) token))
282
+ (done-token (format " PANEL-WORK-DONE /// %s /// %s " (buffer-name ) token))
283
+ (params (lsp-make-copilot-ls-panel-completion-params :text-document (lsp--text-document-identifier)
284
+ :position (lsp--cur-position)
285
+ :partial-result-token partial-token
286
+ :work-done-token done-token)))
287
+ (setq-local lsp-copilot-panel-modification-tick (buffer-chars-modified-tick ))
288
+ (setq-local lsp-copilot-panel-completion-token token)
289
+ (setq-local lsp-copilot-panel-completion-items nil )
290
+
291
+ ; ; call the complation and do not wait for any result -- the completions
292
+ ; ; will be provided via $/progress notifications
293
+ (lsp-request-async " textDocument/copilotPanelCompletion" params #'ignore )))
294
+
295
+
296
+ ; ;; LSP Client
297
+
94
298
(defun lsp-copilot--find-active-workspaces ()
95
299
" Returns a list of lsp-copilot workspaces"
96
300
(-some->> (lsp-session)
@@ -190,13 +394,18 @@ automatically, browse to %s." user-code verification-uri))
190
394
:name " emacs"
191
395
:version " 0.1.0" ))
192
396
397
+ (defun lsp-copilot--progress-callback (workspace params )
398
+ (when lsp-progress-function
399
+ (funcall lsp-progress-function workspace params))
400
+
401
+ (lsp-copilot--panel-completions-progress-handler workspace params))
402
+
193
403
(defun lsp-copilot--server-initialized-fn (workspace )
194
404
; ; Patch capabilities -- server may respond with an empty dict. In plist,
195
405
; ; this would become nil
196
406
(let ((caps (lsp--workspace-server-capabilities workspace)))
197
407
(lsp:set-server-capabilities-inline-completion-provider? caps t ))
198
408
199
-
200
409
(when lsp-copilot-auth-check-delay
201
410
(run-at-time lsp-copilot-auth-check-delay
202
411
nil
@@ -224,7 +433,7 @@ automatically, browse to %s." user-code verification-uri))
224
433
:download-server-fn (lambda (_client callback error-callback _update? )
225
434
(lsp-package-ensure 'copilot-ls callback error-callback))
226
435
:notification-handlers (lsp-ht
227
- (" $/progress" ( lambda ( &rest args ) ( lsp-message " $/ progress with %S " args)) )
436
+ (" $/progress" # ' lsp-copilot-- progress-callback )
228
437
(" featureFlagsNotification" #'ignore )
229
438
(" statusNotification" #'ignore )
230
439
(" didChangeStatus" #'ignore )
0 commit comments