-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathcopilot.el
165 lines (147 loc) · 6.12 KB
/
copilot.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
;;; copilot.el --- Emacs Copilot
;; Copyright 2023 Justine Alexandra Roberts Tunney
;; Author: Justine Tunney
;; Email: [email protected]
;; License: Apache 2.0
;; Version: 0.1
;; Copyright 2023 Mozilla Foundation
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;;; Commentary:
;;
;; The `copilot-complete' function demonstrates that ~100 lines of LISP
;; is all it takes for Emacs to do that thing Github Copilot and VSCode
;; are famous for doing except superior w.r.t. both quality and freedom
;;
;; Emacs Copilot helps you do pair programming with a local-running LLM
;; that generates code completions within Emacs buffers. The LLM is run
;; as a sub-command that remembers your local editing history on a file
;; by file basis. Tokens stream into your buffer without delay as gen'd
;; and you can hit `C-g' to interrupt your LLM at any time. History and
;; memory can also be deleted from the LLM's context when deleting code
;; from your Emacs buffer that matches up verbatim. Copilot is language
;; agnostic and your programming language is determed by file extension
;;
;; The recommended LLM right now is WizardCoder 34b since it scores the
;; same as GPT-4 on HumanEval. You need a computer like a Mac Studio M2
;; Ultra in order to use it. If you have a modest system then you could
;; consider downloading the WizardCoder-Python-13b llamafile since it's
;; almost as good, and will even go acceptably fast on CPU-only systems
;; having at least AVX2 and 2200 MT/s RAM. If you're even more strapped
;; for compute and use things like Raspberry Pi, then give Phi-2 a spin
;;
;; To get started, try writing the first line of a function, e.g.
;;
;; bool is_prime(int x) {
;;
;; Then place your caret at the end of the line, and press `C-c C-k` to
;; hand over control to your LLM, which should generate the rest of the
;; function implementation for you. Things are also tuned so the LLM is
;; likely to stop as soon as a function is made. Explanations and other
;; kind of ELI5 commentary is avoided too.
;;
;; Later on, if you were to write something like this:
;;
;; int main() {
;; for (int i = 0; i < 100;
;;
;; And ask your LLM to complete that, then your LLM will likely recall
;; that you two wrote an is_prime() function earlier, even though it's
;; only considering those two lines in the current instruction. You'll
;; most likely then see it decide to generate code to print the primes
;;; Code:
(defgroup copilot nil
"Large language model code completion."
:prefix "copilot-"
:group 'editing)
(defcustom copilot-bin
"wizardcoder-python-34b-v1.0.Q5_K_M.llamafile"
"Path of llamafile executable with LLM weights."
:type 'string
:group 'copilot)
;;;###autoload
(defun copilot-complete ()
(interactive)
(let* ((spot (point))
(inhibit-quit t)
(curfile (buffer-file-name))
(cash (concat curfile ".cache"))
(hist (concat curfile ".prompt"))
(lang (file-name-extension curfile))
;; extract current line, to left of caret
;; and the previous line, to give the llm
(code (save-excursion
(dotimes (i 2)
(when (> (line-number-at-pos) 1)
(previous-line)))
(beginning-of-line)
(buffer-substring-no-properties (point) spot)))
;; create new prompt for this interaction
(system "\
You are an Emacs code generator. \
Writing comments is forbidden. \
Writing test code is forbidden. \
Writing English explanations is forbidden. ")
(prompt (format
"[INST]%sGenerate %s code to complete:[/INST]\n```%s\n%s"
(if (file-exists-p cash) "" system) lang lang code)))
;; iterate text deleted within editor then purge it from prompt
(when kill-ring
(save-current-buffer
(find-file hist)
(dotimes (i 10)
(let ((substring (current-kill i t)))
(when (and substring (string-match-p "\n.*\n" substring))
(goto-char (point-min))
(while (search-forward substring nil t)
(delete-region (- (point) (length substring)) (point))))))
(save-buffer 0)
(kill-buffer (current-buffer))))
;; append prompt for current interaction to the big old prompt
(write-region prompt nil hist 'append 'silent)
;; run llamafile streaming stdout into buffer catching ctrl-g
(with-local-quit
(call-process copilot-bin nil (list (current-buffer) nil) t
"--prompt-cache" cash
"--prompt-cache-all"
"--silent-prompt"
"--temp" "0"
"-c" "1024"
"-ngl" "35"
"-r" "```"
"-r" "\n}"
"-f" hist))
;; get rid of most markdown syntax
(let ((end (point)))
(save-excursion
(goto-char spot)
(while (search-forward "\\_" end t)
(backward-char)
(delete-backward-char 1 nil)
(setq end (- end 1)))
(goto-char spot)
(while (search-forward "```" end t)
(delete-backward-char 3 nil)
(setq end (- end 3))))
;; append generated code to prompt
(write-region spot end hist 'append 'silent))))
;; define `ctrl-c ctrl-k` keybinding for llm code completion
(defun copilot-c-hook ()
(define-key c-mode-base-map (kbd "C-c C-k") 'copilot-complete))
(add-hook 'c-mode-common-hook 'copilot-c-hook)
(defun copilot-py-hook ()
(define-key python-mode-map (kbd "C-c C-k") 'copilot-complete))
(add-hook 'python-common-hook 'copilot-py-hook)
(global-set-key (kbd "C-c C-k") 'copilot-complete)
(provide 'copilot)
;;; ansi-mode.el ends here