diff --git a/copilot-balancer.el b/copilot-balancer.el index c5f108d..61a9ff7 100644 --- a/copilot-balancer.el +++ b/copilot-balancer.el @@ -176,12 +176,13 @@ Special care has to be taken to ignore pairs in the middle of strings." (defvar copilot-balancer-top-level-form-start-regexp (rx line-start (or (literal "(") (literal "[") (literal "{"))) - "Regexp for the start of a top level form. Assumes cursor is at the start of a line.") + "Regexp for the start of a top level form. Assumes cursor is at the start +of a line.") (defvar copilot-balancer-form-end-regexp (rx (or (literal "}") (literal "]") (literal ")")) line-end) - "Regexp for the end of a form. Assumes cursor is at the last character of the line -(not the actual newline character).") + "Regexp for the end of a form. Assumes cursor is at the last character of +the line (not the actual newline character).") (defun copilot-balancer-get-top-level-form-beginning-to-point (x) (save-excursion diff --git a/copilot.el b/copilot.el index 6832872..29293a6 100644 --- a/copilot.el +++ b/copilot.el @@ -102,9 +102,9 @@ indentation offset." :type 'directory :group 'copilot) -(defconst copilot--dist-dir +(defconst copilot--server-executable (if (eq system-type 'windows-nt) - (f-join copilot-install-dir "node_modules" "copilot-node-server" "copilot" "dist") + (f-join copilot-install-dir "node_modules" "copilot-node-server" "copilot" "dist" "agent.js") (f-join copilot-install-dir "bin" "copilot-node-server")) "The dist directory containing agent.js file.") @@ -119,6 +119,9 @@ You may adjust this variable at your own risk." (defvar-local copilot--overlay nil "Overlay for Copilot completion.") +(defvar-local copilot--keymap-overlay nil + "Overlay used to surround point and make copilot-completion-keymap activate.") + (defvar copilot--connection nil "Copilot agent jsonrpc connection instance.") @@ -205,7 +208,7 @@ Please upgrade the server via `M-x copilot-reinstall-server`")) :notification-dispatcher #'copilot--handle-notification :process (make-process :name "copilot agent" :command (list copilot-node-executable - (concat copilot--dist-dir "/agent.js")) + copilot--server-executable) :coding 'utf-8-emacs-unix :connection-type 'pipe :stderr (get-buffer-create "*copilot stderr*") @@ -346,8 +349,9 @@ automatically, browse to %s." user-code verification-uri)) (setq mode (get mode 'derived-mode-parent)))) (when mode (cl-some (lambda (s) - (when (and (boundp s) (numberp (symbol-value s))) - (symbol-value s))) + ;; s can be a symbol or a number. + (cond ((numberp s) s) + ((and (boundp s) (numberp (symbol-value s))) (symbol-value s)))) (alist-get mode copilot-indentation-alist)))) (progn (when (and @@ -553,12 +557,20 @@ To work around posn problems with after-string property.") (defconst copilot-completion-map (make-sparse-keymap) "Keymap for Copilot completion overlay.") +(defun copilot--get-or-create-keymap-overlay () + "Make or return the local copilot--keymap-overlay." + (unless (overlayp copilot--keymap-overlay) + (setq copilot--keymap-overlay (make-overlay 1 1 nil nil t)) + (overlay-put copilot--keymap-overlay 'keymap copilot-completion-map) + (overlay-put copilot--keymap-overlay 'priority 101)) + copilot--keymap-overlay) + (defun copilot--get-overlay () "Create or get overlay for Copilot." (unless (overlayp copilot--overlay) (setq copilot--overlay (make-overlay 1 1 nil nil t)) - (overlay-put copilot--overlay 'keymap copilot-completion-map) - (overlay-put copilot--overlay 'priority 100)) + (overlay-put + copilot--overlay 'keymap-overlay (copilot--get-or-create-keymap-overlay))) copilot--overlay) (defun copilot--overlay-end (ov) @@ -568,6 +580,16 @@ To work around posn problems with after-string property.") (defun copilot--set-overlay-text (ov completion) "Set overlay OV with COMPLETION." (move-overlay ov (point) (line-end-position)) + + ;; set overlay position for the keymap, to activate copilot-completion-map + ;; + ;; if the point is at the end of the buffer, we will create a + ;; 0-length buffer. But this is ok, since the keymap will still + ;; activate _so long_ as no other overlay contains the point. + ;; + ;; see https://github.com/copilot-emacs/copilot.el/issues/251 for details. + (move-overlay (overlay-get ov 'keymap-overlay) (point) (min (point-max) (+ 1 (point)))) + (let* ((tail (buffer-substring (copilot--overlay-end ov) (line-end-position))) (p-completion (concat (propertize completion 'face 'copilot-overlay-face) tail))) @@ -608,6 +630,7 @@ already saving an excursion. This is also a private function." (copilot--async-request 'notifyRejected (list :uuids `[,(overlay-get copilot--overlay 'uuid)]))) (delete-overlay copilot--overlay) + (delete-overlay copilot--keymap-overlay) (setq copilot--real-posn nil))) (defun copilot-accept-completion (&optional transform-fn) @@ -899,35 +922,45 @@ command that triggered `post-command-hook'." (defun copilot-installed-version () "Return the version number of currently installed `copilot-node-server'." (when-let* - ((package-json (f-join copilot-install-dir "node_modules" "copilot-node-server" "package.json")) + ((package-json + (if (eq system-type 'windows-nt) + (f-join copilot-install-dir "node_modules" "copilot-node-server" "package.json") + (f-join copilot-install-dir "lib" "node_modules" "copilot-node-server" "package.json"))) ((file-exists-p package-json))) (with-temp-buffer (insert-file-contents package-json) - (goto-char (point-min)) - (when (search-forward "\"version\": \"" nil t) - (buffer-substring-no-properties (point) (- (line-end-position) 2)))))) + (save-match-data + (when (re-search-forward "\"version\": \"\\([0-9]+\.[0-9]+\.[0-9]+\\)") + (match-string 1)))))) ;; XXX: This function is modified from `lsp-mode'; see `lsp-async-start-process' ;; function for more information. (defun copilot-async-start-process (callback error-callback &rest command) "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." (let ((name (cl-first command))) - (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) - (not (null cmd))) - command) - " ") t - (lambda (&rest _) - (generate-new-buffer-name "*copilot-install-server*"))) + (with-current-buffer + (compilation-start + (mapconcat + #'shell-quote-argument + (-filter + (lambda (cmd) + (not (null cmd))) + command) " ") + t + (lambda (&rest _) + (generate-new-buffer-name "*copilot-install-server*"))) (view-mode +1) (add-hook 'compilation-finish-functions (lambda (_buf status) (if (string= "finished\n" status) - (condition-case err - (funcall callback) - (error - (funcall error-callback (error-message-string err)))) - (funcall error-callback (s-trim-right status)))) + (when callback + (condition-case err + (funcall callback) + (error + (funcall error-callback (error-message-string err))))) + (when error-callback + (funcall error-callback (s-trim-right status))))) nil t)))) ;;;###autoload diff --git a/readme.md b/readme.md index bae20c9..005c656 100644 --- a/readme.md +++ b/readme.md @@ -14,7 +14,7 @@ Retired maintainer: [@zerolfx][]. ## Installation -0. Ensure your Emacs version is at least 27, and the dependency package `editorconfig` ([melpa](https://melpa.org/#/editorconfig)) is also installed. +0. Ensure your Emacs version is at least 27, the dependency package `editorconfig` ([melpa](https://melpa.org/#/editorconfig)) and `jsonrpc` ([elpa](https://elpa.gnu.org/packages/jsonrpc.html), >= 1.0.24) are both installed. 1. Install [Node.js][] v18+. (You can specify the path to `node` executable by setting `copilot-node-executable`.)