@@ -91,6 +91,210 @@ The input are the file name and the major mode of the buffer."
9191 :path " copilot-language-server"
9292 :version lsp-copilot-version))
9393
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+
94298(defun lsp-copilot--find-active-workspaces ()
95299 " Returns a list of lsp-copilot workspaces"
96300 (-some->> (lsp-session)
@@ -190,13 +394,18 @@ automatically, browse to %s." user-code verification-uri))
190394 :name " emacs"
191395 :version " 0.1.0" ))
192396
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+
193403(defun lsp-copilot--server-initialized-fn (workspace )
194404 ; ; Patch capabilities -- server may respond with an empty dict. In plist,
195405 ; ; this would become nil
196406 (let ((caps (lsp--workspace-server-capabilities workspace)))
197407 (lsp:set-server-capabilities-inline-completion-provider? caps t ))
198408
199-
200409 (when lsp-copilot-auth-check-delay
201410 (run-at-time lsp-copilot-auth-check-delay
202411 nil
@@ -224,7 +433,7 @@ automatically, browse to %s." user-code verification-uri))
224433 :download-server-fn (lambda (_client callback error-callback _update? )
225434 (lsp-package-ensure 'copilot-ls callback error-callback))
226435 :notification-handlers (lsp-ht
227- (" $/progress" ( lambda ( &rest args ) ( lsp-message " $/ progress with %S " args)) )
436+ (" $/progress" # ' lsp-copilot-- progress-callback )
228437 (" featureFlagsNotification" #'ignore )
229438 (" statusNotification" #'ignore )
230439 (" didChangeStatus" #'ignore )
0 commit comments