-
Notifications
You must be signed in to change notification settings - Fork 1
/
corsair.el
153 lines (136 loc) · 5.66 KB
/
corsair.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
;;; corsair.el --- Text accumulation enhancements for GPTel -*- lexical-binding: t; -*-
;; Author: Robert Kirby <[email protected]>
;; Maintainer: Robert Kirby <[email protected]>
;; Version: 0.1
;; Package-Requires: ((emacs "28.1") (gptel "0.9.0"))
;; Homepage: https://github.com/rob137/Corsair
;; Keywords: convenience, tools
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;; Corsair provides enhancements for GPTel, allowing for context accumulation
;; and file expansion within Emacs projects.
;; Suggested Key bindings:
;; (global-set-key (kbd "C-c g c") #'corsair-open-chat-buffer) ;; Open chat buffer
;; (global-set-key (kbd "C-c g a c") #'corsair-accumulate-file-path-and-contents) ;; Accumulate file path and contents
;; (global-set-key (kbd "C-c g a n") #'corsair-accumulate-file-name) ;; Accumulate file name
;; (global-set-key (kbd "C-c g a v") #'corsair-accumulate-file-path) ;; Accumulate file path
;; (global-set-key (kbd "C-c g a w") #'corsair-accumulate-selected-text) ;; Accumulate selected text
;; (global-set-key (kbd "C-c g a D") #'corsair-drop-accumulated-buffer) ;; Drop chat buffer
;; (global-set-key (kbd "C-c g f") #'corsair-insert-file-or-folder-contents) ;; Insert file or folder contents
;;; Code:
(require 'project)
(require 'cl-lib)
(require 'subr-x)
(require 'gptel)
(defgroup corsair nil
"Enhancements for GPTel with context accumulation and file expansion."
:group 'tools
:prefix "corsair-")
(defcustom corsair-chat-buffer-name "*GPTel Chat*"
"Name of the GPTel chat buffer."
:type 'string
:group 'corsair)
;;;###autoload
(defun corsair-open-chat-buffer ()
"Open or switch to the GPTel chat buffer and set it up for GPTel."
(interactive)
(let ((buffer (get-buffer-create corsair-chat-buffer-name)))
(with-current-buffer buffer
(unless (derived-mode-p 'org-mode)
(org-mode))
(gptel-mode))
(switch-to-buffer buffer)))
;;;###autoload
(defun corsair-accumulate-file-path-and-contents ()
"Append file path and contents to the GPTel chat buffer."
(interactive)
(cond
(buffer-file-name
(let* ((path (buffer-file-name))
(contents (buffer-string))
(data (concat "\n" path "\n" contents "\n")))
(with-current-buffer (get-buffer-create corsair-chat-buffer-name)
(goto-char (point-max))
(insert data))))
((user-error "No file associated with this buffer"))))
;;;###autoload
(defun corsair-accumulate-file-name ()
"Append file name to the GPTel chat buffer."
(interactive)
(cond
(buffer-file-name
(let ((name (file-name-nondirectory buffer-file-name)))
(with-current-buffer (get-buffer-create corsair-chat-buffer-name)
(goto-char (point-max))
(insert "\n" name "\n"))))
((user-error "No file associated with this buffer"))))
;;;###autoload
(defun corsair-accumulate-file-path ()
"Append file path to the GPTel chat buffer."
(interactive)
(cond
(buffer-file-name
(let ((path buffer-file-name))
(with-current-buffer (get-buffer-create corsair-chat-buffer-name)
(goto-char (point-max))
(insert "\n" path "\n"))))
((user-error "No file associated with this buffer"))))
;;;###autoload
(defun corsair-accumulate-selected-text ()
"Append selected text to the GPTel chat buffer."
(interactive)
(cond
((use-region-p)
(let ((text (buffer-substring-no-properties (region-beginning) (region-end))))
(with-current-buffer (get-buffer-create corsair-chat-buffer-name)
(goto-char (point-max))
(insert "\n" text "\n"))
(deactivate-mark)))
((user-error "No region selected"))))
;;;###autoload
(defun corsair-drop-accumulated-buffer ()
"Clear the contents of the GPTel chat buffer."
(interactive)
(let ((buf (get-buffer corsair-chat-buffer-name)))
(if buf
(with-current-buffer buf
(erase-buffer))
(message "GPTel chat buffer does not exist."))))
(defun corsair-project-paths ()
"Return a list of files in the current project."
(let ((project (project-current t)))
(if project
(project-files project)
(user-error "No project found"))))
;;;###autoload
(defun corsair-insert-file-or-folder-contents ()
"Insert contents of a selected file / directory from project into GPTel Chat."
(interactive)
(let ((project (project-current t)))
(if project
(let* ((project-root (project-root project))
(project-paths (corsair-project-paths))
(path (completing-read "Select file or directory: " project-paths))
(full-path (expand-file-name path project-root)))
(with-current-buffer (get-buffer-create corsair-chat-buffer-name)
(goto-char (point-max))
(cond
((file-directory-p full-path)
(insert (format "\nDirectory: %s\n" path))
(dolist (file (directory-files-recursively full-path ".*" nil nil))
(insert (format "\n%s\n%s\n"
(file-relative-name file project-root)
(with-temp-buffer
(insert-file-contents file)
(buffer-string))))))
((file-regular-p full-path)
(insert (format "\n%s\n%s\n" path
(with-temp-buffer
(insert-file-contents full-path)
(buffer-string)))))
(t
(user-error "Selected path is neither a file nor a directory"))))
(message "Inserted contents of %s" path))
(user-error "No project found"))))
(provide 'corsair)
;;; corsair.el ends here