|
6 | 6 | ;; Maintainer: Shen, Jen-Chieh <[email protected]>
|
7 | 7 | ;; URL: https://github.com/emacs-openai/codegpt
|
8 | 8 | ;; Version: 0.1.0
|
9 |
| -;; Package-Requires: ((emacs "26.1") (openai "0.1.0")) |
| 9 | +;; Package-Requires: ((emacs "26.1") (openai "0.1.0") (spinner "1.7.4")) |
10 | 10 | ;; Keywords: convenience codegpt
|
11 | 11 |
|
12 | 12 | ;; This file is not part of GNU Emacs.
|
|
31 | 31 |
|
32 | 32 | ;;; Code:
|
33 | 33 |
|
| 34 | +(require 'cl-lib) |
| 35 | + |
34 | 36 | (require 'openai)
|
| 37 | +(require 'openai-chat) |
35 | 38 | (require 'openai-completion)
|
| 39 | +(require 'spinner) |
36 | 40 |
|
37 | 41 | (defgroup codegpt nil
|
38 | 42 | "Use GPT-3 tp help you write code."
|
|
58 | 62 | :type 'list
|
59 | 63 | :group 'codegpt)
|
60 | 64 |
|
| 65 | +(defcustom codegpt-tunnel 'completion |
| 66 | + "Tunnel to use for the tasks." |
| 67 | + :type '(choice (const :tag "Through Completion" completion) |
| 68 | + (const :tag "Through ChatGPT" chat)) |
| 69 | + :group 'codegpt) |
| 70 | + |
61 | 71 | (defcustom codegpt-model "text-davinci-003"
|
62 | 72 | "ID of the model to use."
|
63 | 73 | :type 'string
|
|
73 | 83 | :type 'number
|
74 | 84 | :group 'openai)
|
75 | 85 |
|
| 86 | +(defcustom codegpt-spinner-type 'moon |
| 87 | + "The type of the spinner." |
| 88 | + :type '(choice (const :tag "Key to variable `spinner-types'" symbol) |
| 89 | + (const :tag "Vector of characters" vector)) |
| 90 | + :group 'openai) |
| 91 | + |
| 92 | +(defvar codegpt-requesting-p nil |
| 93 | + "Non-nil if sitll requesting.") |
| 94 | + |
| 95 | +(defvar codegpt-spinner-counter 0 |
| 96 | + "Spinner counter.") |
| 97 | + |
| 98 | +(defvar codegpt-spinner-timer nil |
| 99 | + "Spinner timer.") |
| 100 | + |
| 101 | +;; |
| 102 | +;;; Major Mode |
| 103 | + |
| 104 | +(defun codegpt-header-line () |
| 105 | + "Header line for CodeGPT." |
| 106 | + (format " %s[Tunnel] %s, [Model] %s" |
| 107 | + (if codegpt-requesting-p |
| 108 | + (let* ((spinner (if (symbolp codegpt-spinner-type) |
| 109 | + (cdr (assoc codegpt-spinner-type spinner-types)) |
| 110 | + codegpt-spinner-type)) |
| 111 | + (len (length spinner))) |
| 112 | + (when (<= len codegpt-spinner-counter) |
| 113 | + (setq codegpt-spinner-counter 0)) |
| 114 | + (format "%s " (elt spinner codegpt-spinner-counter))) |
| 115 | + "") |
| 116 | + codegpt-tunnel codegpt-model)) |
| 117 | + |
| 118 | +(defun codegpt-mode--cancel-timer () |
| 119 | + "Cancel spinner timer." |
| 120 | + (when (timerp codegpt-spinner-timer) |
| 121 | + (cancel-timer codegpt-spinner-timer))) |
| 122 | + |
| 123 | +;;;###autoload |
| 124 | +(define-derived-mode codegpt-mode fundamental-mode "CodeGPT" |
| 125 | + "Major mode for `codegpt-mode'. |
| 126 | +
|
| 127 | +\\<codegpt-mode-map>" |
| 128 | + (setq codegpt-spinner-counter 0) |
| 129 | + (setq-local header-line-format `((:eval (codegpt-header-line)))) |
| 130 | + (add-hook 'kill-buffer-hook #'codegpt-mode--cancel-timer nil t) |
| 131 | + (codegpt-mode--cancel-timer) |
| 132 | + (setq codegpt-spinner-timer (run-with-timer 0.1 |
| 133 | + 0.1 |
| 134 | + (lambda () |
| 135 | + (cl-incf codegpt-spinner-counter) |
| 136 | + (force-mode-line-update))))) |
| 137 | + |
76 | 138 | ;;
|
77 | 139 | ;;; Application
|
78 | 140 |
|
|
82 | 144 | `(progn
|
83 | 145 | (openai--pop-to-buffer codegpt-buffer-name) ; create it
|
84 | 146 | (openai--with-buffer codegpt-buffer-name
|
| 147 | + (codegpt-mode) |
85 | 148 | (erase-buffer)
|
86 | 149 | (insert ,instruction "\n\n")
|
87 | 150 | ,@body)))
|
|
102 | 165 |
|
103 | 166 | The partial code is defined in with the region, and the START nad END are
|
104 | 167 | boundaries of that region in buffer."
|
| 168 | + (setq codegpt-requesting-p t) |
105 | 169 | (let ((text (string-trim (buffer-substring start end)))
|
106 | 170 | (original-window (selected-window)))
|
107 | 171 | (codegpt--ask-in-buffer instruction
|
108 | 172 | (insert text "\n\n")
|
109 |
| - (openai-completion |
110 |
| - (buffer-string) |
| 173 | + (funcall |
| 174 | + (cl-case codegpt-tunnel |
| 175 | + (`completion #'openai-completion) |
| 176 | + (`chat #'openai-chat)) |
| 177 | + (cl-case codegpt-tunnel |
| 178 | + (`completion (buffer-string)) |
| 179 | + (`chat `[(("role" . "user") |
| 180 | + ("content" . ,(buffer-string)))])) |
111 | 181 | (lambda (data)
|
| 182 | + (setq codegpt-requesting-p nil) |
| 183 | + (codegpt-mode--cancel-timer) |
112 | 184 | (openai--with-buffer codegpt-buffer-name
|
113 | 185 | (openai--pop-to-buffer codegpt-buffer-name)
|
114 |
| - (let* ((choices (openai--data-choices data)) |
115 |
| - (result (openai--get-choice choices)) |
116 |
| - (original-point (point))) |
117 |
| - (insert (string-trim result) "\n") |
| 186 | + (let ((original-point (point))) |
| 187 | + (cl-case codegpt-tunnel |
| 188 | + (`completion |
| 189 | + (let* ((choices (openai--data-choices data)) |
| 190 | + (result (openai--get-choice choices))) |
| 191 | + (insert (string-trim result) "\n"))) |
| 192 | + (`chat |
| 193 | + (let ((choices (let-alist data .choices)) |
| 194 | + (result)) |
| 195 | + (mapc (lambda (choice) |
| 196 | + (let-alist choice |
| 197 | + (let-alist .message |
| 198 | + (setq result (string-trim .content))))) |
| 199 | + choices) |
| 200 | + (insert (string-trim result) "\n")))) |
118 | 201 | (codegpt--fill-region original-point (point))))
|
119 | 202 | (unless codegpt-focus-p
|
120 | 203 | (select-window original-window)))
|
|
0 commit comments