Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Install server #247

Merged
merged 22 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Eask
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
(depends-on "dash")
(depends-on "editorconfig")
(depends-on "jsonrpc")

(setq package-lint-batch-fail-on-warnings nil)
(depends-on "f")

(setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432
21 changes: 15 additions & 6 deletions copilot-balancer.el
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
;; -*- lexical-binding: t -*-
;;; copilot-balancer.el --- Balancer module -*- lexical-binding:t -*-

;;; Commentary:

;; Balancer module

;;; Code:

(require 'cl-lib)
(require 'pcase)
(require 'dash)
(require 'rx)

(defvar copilot-balancer-lisp-modes '(emacs-lisp-mode
lisp-mode
scheme-mode
clojure-mode)
(require 'dash)

(defvar copilot-balancer-lisp-modes '( emacs-lisp-mode
lisp-mode
lisp-interaction-mode
scheme-mode
clojure-mode)
"List of lisp modes to balance.")

(defvar copilot-balancer-lisp-pairs
Expand Down Expand Up @@ -329,3 +337,4 @@ the line (not the actual newline character).")
(t (list start end completion)))))

(provide 'copilot-balancer)
;;; copilot-balancer.el ends here
152 changes: 127 additions & 25 deletions copilot.el
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
;;; copilot.el --- An unofficial Copilot plugin for Emacs -*- lexical-binding:t -*-

;; Package-Requires: ((emacs "27.2") (s "1.12.0") (dash "2.19.1") (editorconfig "0.8.2") (jsonrpc "1.0.23"))
;; Package-Requires: ((emacs "27.2") (s "1.12.0") (dash "2.19.1") (editorconfig "0.8.2") (jsonrpc "1.0.23") (f "0.20.0"))
;; Version: 0.0.1
;;; URL: https://github.com/copilot-emacs/copilot.el
;; URL: https://github.com/copilot-emacs/copilot.el

;;; Commentary:

;; An unofficial Copilot plugin for Emacs

;;; Code:

(require 'cl-lib)
(require 'compile)
(require 'json)
(require 'jsonrpc)

(require 'f)
(require 's)
(require 'dash)
(require 'editorconfig)
Expand Down Expand Up @@ -54,10 +58,7 @@ Enabling event logging may slightly affect performance."
:group 'copilot
:type 'integer)

(defcustom copilot-node-executable
(if (eq system-type 'windows-nt)
"node.exe"
"node")
(defcustom copilot-node-executable (executable-find "node")
"Node executable path."
:group 'copilot
:type 'string)
Expand Down Expand Up @@ -91,14 +92,30 @@ indentation offset."
:type '(alist :key-type symbol :value-type (choice integer symbol))
:group 'copilot)

(defconst copilot--base-dir
(file-name-directory
(or load-file-name
(buffer-file-name)))
"Directory containing this file.")
(defconst copilot-server-package-name "copilot-node-server"
"The name of the package to install copilot server.")

(defcustom copilot-install-dir (expand-file-name
(locate-user-emacs-file (f-join ".cache" "copilot")))
"Directory in which the servers will be installed."
:risky t
:type 'directory
:group 'copilot)

(defconst copilot--server-executable
(if (eq system-type 'windows-nt)
(f-join copilot-install-dir "node_modules" "copilot-node-server" "copilot"
"bin" "copilot-node-server")
emil-vdw marked this conversation as resolved.
Show resolved Hide resolved
(f-join copilot-install-dir "bin" "copilot-node-server"))
"The dist directory containing agent.js file.")

(defcustom copilot-version "1.14.0"
"Copilot version.

(defconst copilot-version "0.10.0"
"Copilot version.")
The default value is the preferred version and ensures functionality.
You may adjust this variable at your own risk."
:type 'string
:group 'copilot)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made this variable customizable so the user can choose what version to install.


(defvar-local copilot--overlay nil
"Overlay for Copilot completion.")
Expand Down Expand Up @@ -168,8 +185,15 @@ indentation offset."

(defun copilot--start-agent ()
"Start the copilot agent process in local."
(if (not (locate-file copilot-node-executable exec-path))
(user-error "Could not find node executable")
(cond
((null copilot-node-executable)
(user-error "Could not find node executable"))
((not (file-exists-p copilot-install-dir))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This did not catch the condition correctly when I uninstalled the copilot server (this directory is still present). Maybe make it more reliable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did you uninstall the server? 🤔 The copilot-install-dir will be gone if you uninstall it through M-x copilot-uninstall-server.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that makes sense. I did:

npm -g --prefix ~/.emacs.d/.cache/copilot uninstall copilot-node-server

(user-error "Server is not installed, please install via `M-x copilot-install-server`"))
(t
(unless (equal (copilot-installed-version) copilot-version)
(warn "Newer versions of the Copilot server are available for installation.
Please upgrade the server via `M-x copilot-reinstall-server`"))
(let ((node-version (->> (with-output-to-string
(call-process copilot-node-executable nil standard-output nil "--version"))
(s-trim)
Expand All @@ -185,7 +209,7 @@ indentation offset."
:notification-dispatcher #'copilot--handle-notification
:process (make-process :name "copilot agent"
:command (list copilot-node-executable
(concat copilot--base-dir "/dist/agent.js"))
copilot--server-executable)
:coding 'utf-8-emacs-unix
:connection-type 'pipe
:stderr (get-buffer-create "*copilot stderr*")
Expand All @@ -194,9 +218,9 @@ indentation offset."
(copilot--request 'initialize '(:capabilities (:workspace (:workspaceFolders t))))
(copilot--async-request 'setEditorInfo
`(:editorInfo (:name "Emacs" :version ,emacs-version)
:editorPluginInfo (:name "copilot.el" :version ,copilot-version)
,@(when copilot-network-proxy
`(:networkProxy ,copilot-network-proxy)))))))))
:editorPluginInfo (:name "copilot.el" :version ,copilot-version)
,@(when copilot-network-proxy
`(:networkProxy ,copilot-network-proxy))))))))))

;;
;; login / logout
Expand Down Expand Up @@ -655,8 +679,8 @@ Use TRANSFORM-FN to transform completion if provided."
(when (copilot--satisfy-display-predicates)
(copilot--dbind
(:text :uuid :docVersion doc-version
:range (:start (:line :character start-char)
:end (:character end-char)))
:range (:start (:line :character start-char)
:end (:character end-char)))
completion-data
(when (= doc-version copilot--doc-version)
(save-excursion
Expand Down Expand Up @@ -864,10 +888,10 @@ Use this for custom bindings in `copilot-mode'.")
(cancel-timer copilot--post-command-timer))
(when (numberp copilot-idle-delay)
(setq copilot--post-command-timer
(run-with-idle-timer copilot-idle-delay
nil
#'copilot--post-command-debounce
(current-buffer))))))
(run-with-idle-timer copilot-idle-delay
nil
#'copilot--post-command-debounce
(current-buffer))))))
emil-vdw marked this conversation as resolved.
Show resolved Hide resolved

(defun copilot--self-insert (command)
"Handle the case where the char just inserted is the start of the completion.
Expand All @@ -893,5 +917,83 @@ command that triggered `post-command-hook'."
(copilot--satisfy-trigger-predicates))
(copilot-complete)))

;;
;;; Installation

(defun copilot-installed-version ()
"Return the version number of currently installed `copilot-node-server'."
(when-let*
((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)
(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*")))
(view-mode +1)
(add-hook
'compilation-finish-functions
(lambda (_buf status)
(if (string= "finished\n" 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
(defun copilot-install-server ()
"Interactively install server."
(interactive)
(if-let ((npm-binary (executable-find "npm")))
(progn
(make-directory copilot-install-dir 'parents)
(copilot-async-start-process
nil nil
npm-binary
"-g" "--prefix" copilot-install-dir
"install" (format "%s@%s" copilot-server-package-name copilot-version)))
(message "Unable to install %s via `npm' because it is not present" package)
nil))

;;;###autoload
(defun copilot-reinstall-server ()
"Interactively re-install server."
(interactive)
(copilot-uninstall-server)
(copilot-install-server))

;;;###autoload
(defun copilot-uninstall-server ()
"Delete a Copilot server from `copilot-install-dir'."
(interactive)
(unless (file-directory-p copilot-install-dir)
(user-error "Couldn't find %s directory" copilot-install-dir))
(delete-directory copilot-install-dir 'recursive)
(message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name copilot-install-dir))))

(provide 'copilot)
;;; copilot.el ends here
24 changes: 17 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ Copilot.el is an Emacs plugin for GitHub Copilot.

**Warning:** This plugin is unofficial and based on binaries provided by [copilot.vim](https://github.com/github/copilot.vim).

**Note:** You need access to [GitHub Copilot](https://github.com/features/copilot) to use this plugin.
**Note:** You need access to [GitHub Copilot][] to use this plugin.

Current maintainer: [@emil-vdw](https://github.com/emil-vdw), [@rakotomandimby](https://github.com/rakotomandimby).
Current maintainer: [@emil-vdw][], [@rakotomandimby][].
jcs090218 marked this conversation as resolved.
Show resolved Hide resolved

Retired maintainer: [@zerolfx](https://github.com/zerolfx).
Retired maintainer: [@zerolfx][].

## Installation

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](https://nodejs.org/en/download/) v18+. (You can specify the path to `node` executable by setting `copilot-node-executable`.)
1. Install [Node.js][] v18+. (You can specify the path to `node` executable by setting `copilot-node-executable`.)

2. Setup `copilot.el` as described in the next section.

Expand Down Expand Up @@ -156,7 +156,7 @@ Please make sure you have these dependencies installed (available in ELPA/MELPA)

After installing those, clone this repository then insert the below snippet into your config file.

```
```elisp
(add-to-list 'load-path "/path/to/copilot.el")
(require 'copilot)
```
Expand Down Expand Up @@ -232,13 +232,13 @@ Cycle through the completion list.

#### copilot-logout

Logout from GitHub.
Log out from GitHub.

## Customization

#### copilot-node-executable

The executable path of Node.js.
The executable path of [Node.js][].

#### copilot-idle-delay

Expand Down Expand Up @@ -302,3 +302,13 @@ These projects helped me a lot:
+ https://github.com/TommyX12/company-tabnine/
+ https://github.com/cryptobadger/flight-attendant.el
+ https://github.com/github/copilot.vim


<!-- Links -->

[@emil-vdw]: https://github.com/emil-vdw
[@rakotomandimby]: https://github.com/rakotomandimby
jcs090218 marked this conversation as resolved.
Show resolved Hide resolved
[@zerolfx]: https://github.com/zerolfx

[GitHub Copilot]: https://github.com/features/copilot
[Node.js]: https://nodejs.org/en/download/