88; ; Rakotomandimby Mihamina <[email protected] >99; ; Bozhidar Batsov <[email protected] >1010; ; URL: https://github.com/copilot-emacs/copilot.el
11- ; ; Package-Requires: ((emacs "27.2") (editorconfig "0.8.2") (jsonrpc "1.0.14") (f "0.20.0"))
11+ ; ; Package-Requires: ((emacs "27.2") (editorconfig "0.8.2") (jsonrpc "1.0.14") (f "0.20.0") (track-changes "1.4") )
1212; ; Version: 0.3.0-snapshot
1313; ; Keywords: convenience copilot
1414
4848(require 'editorconfig )
4949
5050(require 'copilot-balancer )
51+ (require 'track-changes )
5152
5253(defgroup copilot nil
5354 " Copilot."
@@ -705,6 +706,14 @@ automatically, browse to %s." user-code verification-uri))
705706 (or (copilot--get-minor-mode-language-id)
706707 (copilot--get-major-mode-language-id)))
707708
709+ (defun copilot--lsp-pos (&optional pos )
710+ " Return an LSP position plist for buffer POS.
711+ POS defaults to point."
712+ (save-excursion
713+ (when pos (goto-char pos))
714+ (list :line (- (line-number-at-pos ) copilot--line-bias)
715+ :character (- (point ) (line-beginning-position )))))
716+
708717(defun copilot--generate-doc ()
709718 " Generate doc parameters for completion request."
710719 (save-restriction
@@ -719,8 +728,7 @@ automatically, browse to %s." user-code verification-uri))
719728 :uri (copilot--get-uri)
720729 :relativePath (copilot--get-relative-path)
721730 :languageId (copilot--get-language-id)
722- :position (list :line (- (line-number-at-pos ) copilot--line-bias)
723- :character (- (point ) (line-beginning-position ))))))
731+ :position (copilot--lsp-pos))))
724732
725733(defun copilot--get-completion (callback )
726734 " Get completion with CALLBACK."
@@ -1077,40 +1085,13 @@ provided."
10771085 :version copilot--doc-version
10781086 :text (copilot--get-source)))))))
10791087
1080- (defun copilot--on-doc-change (&optional beg end chars-replaced )
1081- " Notify that the document has changed.
1082-
1083- Arguments BEG, END, and CHARS-REPLACED are metadata for region changed."
1084- (let* ((is-before-change (null chars-replaced))
1085- (is-after-change (not is-before-change))
1086- ; ; for a deletion, the post-change beginning and end are at the same place.
1087- (is-insertion (and is-after-change (not (equal beg end))))
1088- (is-deletion (and is-before-change (not (equal beg end)))))
1089- (when (or is-insertion is-deletion)
1090- (save-restriction
1091- (save-match-data
1092- (widen )
1093- (let* ((range-start (list :line (- (line-number-at-pos beg) copilot--line-bias)
1094- :character (- beg (save-excursion (goto-char beg) (line-beginning-position )))))
1095- (range-end (if is-insertion range-start
1096- (list :line (- (line-number-at-pos end) copilot--line-bias)
1097- :character (- end (save-excursion (goto-char end) (line-beginning-position ))))))
1098- (text (if is-insertion (buffer-substring-no-properties beg end) " " ))
1099- (content-changes (vector (list :range (list :start range-start :end range-end)
1100- :text text))))
1101- (cl-incf copilot--doc-version)
1102- (copilot--notify 'textDocument/didChange
1103- (list :textDocument (list :uri (copilot--get-uri) :version copilot--doc-version)
1104- :contentChanges content-changes))))))))
1105-
11061088(defun copilot--on-doc-close (&rest _args )
11071089 " Notify that the document has been closed."
11081090 (when (seq-contains-p copilot--opened-buffers (current-buffer ))
11091091 (copilot--notify 'textDocument/didClose
11101092 (list :textDocument (list :uri (copilot--get-uri))))
11111093 (setq copilot--opened-buffers (delete (current-buffer ) copilot--opened-buffers))))
11121094
1113-
11141095;;;### autoload
11151096(defun copilot-complete ()
11161097 " Complete at the current point."
@@ -1129,6 +1110,60 @@ Arguments BEG, END, and CHARS-REPLACED are metadata for region changed."
11291110 (when called-interactively
11301111 (copilot--log 'warning " No completion is available." ))))))))
11311112
1113+ ; ;
1114+ ; ; integration with track-changes
1115+ ; ;
1116+
1117+ (defvar-local copilot--track-changes-id nil
1118+ " Tracker id from `track-changes-register' for this buffer." )
1119+
1120+ (defun copilot--lsp-range-end-from-oldtext (beg oldtext )
1121+ " Compute old end position plist for change at BEG replacing OLDTEXT."
1122+ (if (string-empty-p oldtext)
1123+ ; ; Optimization for pure insertions
1124+ (copilot--lsp-pos beg)
1125+ (let* ((start (copilot--lsp-pos beg))
1126+ (start-line (plist-get start :line ))
1127+ (start-char (plist-get start :character ))
1128+ (end-info (with-temp-buffer
1129+ (insert oldtext)
1130+ (goto-char (point-max ))
1131+ (cons (1- (line-number-at-pos ))
1132+ (current-column ))))
1133+ (num-newlines (car end-info))
1134+ (end-char (cdr end-info)))
1135+ (list :line (+ start-line num-newlines)
1136+ :character (if (= num-newlines 0 )
1137+ (+ start-char (length oldtext))
1138+ end-char)))))
1139+
1140+ (defun copilot--track-changes-signal (id &optional _distance )
1141+ " Handle track changes signal for given tracker ID.
1142+ Fetch changes and notify the language server."
1143+ (condition-case err
1144+ (save-restriction
1145+ (widen )
1146+ (track-changes-fetch
1147+ id
1148+ (lambda (beg end before )
1149+ (unless (eq before 'error )
1150+ (save-restriction
1151+ (widen )
1152+ (let* ((new-text (buffer-substring-no-properties beg end))
1153+ (start-pos (copilot--lsp-pos beg))
1154+ (end-pos (copilot--lsp-range-end-from-oldtext beg (or before " " ))))
1155+ (cl-incf copilot--doc-version)
1156+ (copilot--notify
1157+ 'textDocument/didChange
1158+ (list :textDocument (list :uri (copilot--get-uri)
1159+ :version copilot--doc-version)
1160+ :contentChanges
1161+ (vector
1162+ (list :range (list :start start-pos :end end-pos)
1163+ :text new-text))))))))))
1164+ (error
1165+ (copilot--log 'error " Change fetch failed: %s" (error-message-string err)))))
1166+
11321167; ;
11331168; ; minor mode
11341169; ;
@@ -1249,24 +1284,26 @@ Use this for custom bindings in `copilot-mode'.")
12491284(defun copilot--mode-setup ()
12501285 " Set up copilot mode."
12511286 (add-hook 'post-command-hook #'copilot--post-command nil 'local )
1252- (add-hook 'before-change-functions #'copilot--on-doc-change nil 'local )
1253- (add-hook 'after-change-functions #'copilot--on-doc-change nil 'local )
12541287 ; ; Hook onto both window-selection-change-functions and window-buffer-change-functions
12551288 ; ; since both are separate ways of 'focussing' a buffer.
12561289 (add-hook 'window-selection-change-functions #'copilot--on-doc-focus nil 'local )
12571290 (add-hook 'window-buffer-change-functions #'copilot--on-doc-focus nil 'local )
12581291 (add-hook 'kill-buffer-hook #'copilot--on-doc-close nil 'local )
1292+ (unless copilot--track-changes-id
1293+ (setq copilot--track-changes-id
1294+ (track-changes-register #'copilot--track-changes-signal )))
12591295 ; ; The mode may be activated manually while focus remains on the current window/buffer.
12601296 (copilot--on-doc-focus (selected-window )))
12611297
12621298(defun copilot--mode-teardown ()
12631299 " Tear down copilot mode."
12641300 (remove-hook 'post-command-hook #'copilot--post-command 'local )
1265- (remove-hook 'before-change-functions #'copilot--on-doc-change 'local )
1266- (remove-hook 'after-change-functions #'copilot--on-doc-change 'local )
12671301 (remove-hook 'window-selection-change-functions #'copilot--on-doc-focus 'local )
12681302 (remove-hook 'window-buffer-change-functions #'copilot--on-doc-focus 'local )
12691303 (remove-hook 'kill-buffer-hook #'copilot--on-doc-close 'local )
1304+ (when copilot--track-changes-id
1305+ (track-changes-unregister copilot--track-changes-id)
1306+ (setq copilot--track-changes-id nil ))
12701307 ; ; Send the close event for the active buffer since activating the mode will open it again.
12711308 (copilot--on-doc-close))
12721309
0 commit comments