Skip to content

Commit

Permalink
feat: Install server (#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcs090218 authored Feb 17, 2024
1 parent 7a45181 commit 26fa165
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 40 deletions.
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.14"))
;; Package-Requires: ((emacs "27.2") (s "1.12.0") (dash "2.19.1") (editorconfig "0.8.2") (jsonrpc "1.0.14") (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")
(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)

(defvar-local copilot--overlay nil
"Overlay for Copilot completion.")
Expand Down Expand Up @@ -175,7 +192,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 @@ -188,8 +205,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))
(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 @@ -203,9 +227,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 @@ -664,8 +688,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 @@ -873,10 +897,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))))))

(defun copilot--self-insert (command)
"Handle the case where the char just inserted is the start of the completion.
Expand All @@ -902,5 +926,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
25 changes: 18 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][], [@jcs090218][], [@rakotomandimby][].

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,14 @@ 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
[@jcs090218]: https://github.com/jcs090218
[@rakotomandimby]: https://github.com/rakotomandimby
[@zerolfx]: https://github.com/zerolfx

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

0 comments on commit 26fa165

Please sign in to comment.