Emacs AI Code Completion Mode

NAME

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.

SYNOPSIS

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.

DESCRIPTION

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:

  • ai-completion--get-context: Captures text around point (default 1000 chars) to send to the LLM.
  • ai-completion--call-openai: Sends the system message (\"Output code only.\") plus the user prompt to OpenAI’s chat/completions API.
  • ai-completion--extract-code: Simple regex to grab code between triple backticks; fallback if none found.
  • ai-completion-insert: Binds to C-c C-a by default, fetching and inserting the LLM's response.

EXAMPLES

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.

SEE ALSO

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.