ai-completion-mode – a minimal setup to integrate OpenAI-based code completion directly into Emacs. It provides a minor mode, a keybinding, and a straightforward way to fetch AI-generated code.
This blog post demonstrates how to build an “AI code completion” feature in Emacs using the OpenAI API. With a single command (C-c C-a by default), you can send the surrounding code context (or the current region) to an LLM such as GPT-3.5-turbo or GPT-4, then insert the model’s completion right into your buffer.
Below is an abbreviated version of ai-completion.el, a minimal Emacs Lisp script:
;;; ai-completion.el --- Minimal AI code completion in Emacs using OpenAI API (require 'request-deferred) (require 'json) (require 'subr-x) (defgroup ai-completion nil "AI code completion using OpenAI." :group 'tools) (defcustom ai-completion-openai-api-key "" "OpenAI API key for AI code completion." :type 'string :group 'ai-completion) (defcustom ai-completion-model "gpt-3.5-turbo" "OpenAI model (e.g., gpt-3.5-turbo, gpt-4)." :type 'string :group 'ai-completion) (defcustom ai-completion-max-tokens 128 "Max tokens to request." :type 'integer :group 'ai-completion) (defcustom ai-completion-temperature 0.2 "Sampling temperature (0.0 = deterministic)." :type 'float :group 'ai-completion) (defun ai-completion--get-context () (let* ((context-window 1000) (start (max (point-min) (- (point) context-window))) (end (min (point-max) (+ (point) context-window)))) (buffer-substring-no-properties start end))) (defun ai-completion--extract-code (text) "Extract code from TEXT, ignoring explanations. Look for triple backticks; if none, assume the entire TEXT is code." (let ((code-regex \"```\\([^`]*\\)```\")) (if (string-match code-regex text) (match-string 1 text) text))) (defun ai-completion--call-openai (prompt callback) (let ((url \"https://api.openai.com/v1/chat/completions\") (data (json-encode `((\"model\" . ,ai-completion-model) (\"messages\" . [ ((\"role\" . \"system\") (\"content\" . \"You are a coding assistant. Output code only.\")) ((\"role\" . \"user\") (\"content\" . ,prompt)) ]) (\"max_tokens\" . ,ai-completion-max-tokens) (\"temperature\" . ,ai-completion-temperature)))) (headers `((\"Content-Type\" . \"application/json\") (\"Authorization\" . ,(concat \"Bearer \" ai-completion-openai-api-key))))) (request-deferred url :type \"POST\" :data data :headers headers :parser 'json-read :error (lambda (&rest args &key error-thrown &allow-other-keys) (message \"ai-completion error: %S\" error-thrown)) :success (lambda (&key data &allow-other-keys) (let* ((choices (assoc-default 'choices data)) (choice (and choices (aref choices 0))) (msg (assoc-default 'message choice)) (content (assoc-default 'content msg))) (when callback (funcall callback (ai-completion--extract-code content)))))))) (defun ai-completion-insert () \"Fetch an AI completion and insert it at point.\" (interactive) (let ((context (ai-completion--get-context))) (ai-completion--call-openai context (lambda (response) (when response (save-excursion (insert (concat \"\\n\" response))))))))) ;;;###autoload (define-minor-mode ai-completion-mode \"Minor mode for AI code completion.\" :lighter \" AI-Complete\" :keymap (let ((map (make-sparse-keymap))) (define-key map (kbd \"C-c C-a\") 'ai-completion-insert) map) (if ai-completion-mode (message \"ai-completion-mode enabled\") (message \"ai-completion-mode disabled\"))) (provide 'ai-completion) ;;; ai-completion.el ends here
Key points:
chat/completions
API.
C-c C-a
by default, fetching
and inserting the LLM's response.
Installation:
;; In your init.el: ;; 1. Put ai-completion.el on your load path (load-file \"~/.emacs.d/lisp/ai-completion.el\") ;; 2. Set your API key (setq ai-completion-openai-api-key \"sk-XXXXXXXXXXXXXX\") ;; 3. Enable the mode (ai-completion-mode 1)
Now, select some code or place the cursor within code context. Press C-c C-a and the system will insert GPT’s code completion below your cursor, discarding non-code text.
Back to Blog Index | Main Terminal
Refer to the official OpenAI docs for more info on the chat/completions endpoint. For advanced usage—like hooking it into an overlay or integrating with Company/Corfu—feel free to adapt the code further. But this minimal approach is enough to get started with a quick, workable AI code completion mode in Emacs.