forked from ragnard/tabby-mode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tabby-mode.el
127 lines (106 loc) · 4.29 KB
/
tabby-mode.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
;;; tabby-mode.el --- Minor mode for the Tabby AI coding assistant -*- lexical-binding: t -*-
;; Copyright (C) 2023 Authors
;; SPDX-License-Identifier: Apache-2.0
;; Author: Ragnar Dahlén <r.dahlen@gmail.com>
;; URL: https://github.com/ragnard/tabby-mode
;; Package-Requires: ((emacs "25.1"))
;; Version: 1.0
;; Keywords: tools, convenience
;;; Commentary:
;; This package provides a simple integration with the Tabby AI coding
;; assistant. A single interactive function, `tabby-complete`, can be
;; used to send the coding context to a Tabby API instance, and select
;; a suggested code change.
;;; Code:
(require 'json)
(require 'subr-x)
(require 'url)
(require 'url-http)
(eval-when-compile
(defvar url-http-end-of-headers))
(defgroup tabby nil
"Minor mode for the Tabby AI coding assistant."
:link '(url-link "htps://tabby.tabbyml.com")
:group 'programming)
(defcustom tabby-api-url nil
"URL to Tabby API."
:type 'string
:group 'tabby)
(defcustom tabby-completion-function 'completing-read
"Function to use when selecting a completion.
Should have same signature as `completing-read`."
:type 'symbol
:group 'tabby)
(defcustom tabby-mode-language-alist
'((c-mode . "c")
(c++-mode . "cpp")
(go-mode . "go")
(elisp-mode . "elisp")
(java-mode . "java")
(javascript-mode . "javascript")
(kotlin-mode . "kotlin")
(python-mode . "python")
(emacs-lisp-mode . "elisp")
(ruby-mode . "ruby")
(rust-mode . "rust")
(typescript-mode . "typescript")
(yaml-mode . "yaml"))
"Mapping from major mode to Tabby language identifier."
:type '(alist :key-type symbol :value-type string)
:group 'tabby)
(defun tabby--completions-url ()
"Return the API url for completions."
(format "%s/v1/completions" (string-remove-suffix "/" tabby-api-url)))
(defun tabby--completions-request (lang prefix suffix)
"Build a completions request for LANG with PREFIX and SUFFIX."
`((language . ,lang)
(segments . ((prefix . ,prefix)
(suffix . ,suffix)))))
(defun tabby--get-completions (buffer lang prefix suffix callback)
"Async get completions for BUFFER using LANG, PREFIX and SUFFIX.
When the request completes, CALLBACK will be invoked with the response."
(let* ((request (tabby--completions-request lang prefix suffix))
(url-request-method "POST")
(url-request-extra-headers `(("Content-Type" . "application/json")))
(url-request-data (encode-coding-string (json-encode request) 'utf-8)))
(url-retrieve (tabby--completions-url)
(lambda (_status)
(goto-char url-http-end-of-headers)
(let ((response (json-read)))
(funcall callback buffer response))))))
(defun tabby--handle-completion-response (buffer response)
"Handle a completions RESPONSE for a BUFFER."
(let* ((choices (mapcar (lambda (c)
(alist-get 'text c))
(alist-get 'choices response)))
(text (funcall tabby-completion-function "Tabby: " choices)))
(when text
(with-current-buffer buffer
(insert text)))))
(defun tabby--determine-language ()
"Determine the language identifier for the current buffer.
See https://code.visualstudio.com/docs/languages/identifiers."
(alist-get major-mode tabby-mode-language-alist))
(defun tabby-complete ()
"Ask Tabby for completion suggestions for the text around point."
(interactive)
(when (not tabby-api-url)
(error "Please configure the URL for your Tabby server. See customizable variable `tabby-api-url`"))
(let* ((lang (tabby--determine-language))
(selected-text (buffer-substring-no-properties (region-beginning) (region-end)))
(prefix (if (> (length selected-text) 1)
selected-text
(buffer-substring (point-min) (point))))
(suffix (if (> (length selected-text) 1) ""
(unless (eobp)
(buffer-substring (+ (point) 1) (point-max)))
))
)
(if lang
(tabby--get-completions (current-buffer) lang prefix suffix 'tabby--handle-completion-response)
(message "Unable to determine language for current buffer."))))
(define-minor-mode tabby-mode
"A minor mode for the Tabby AI coding assistant."
:keymap '((["C-<tab>"] . tabby-complete)))
(provide 'tabby-mode)
;;; tabby-mode.el ends here