branch: externals/minuet commit 819028cb75c71bda5d619cdf10f58898b3a1cda4 Author: Milan Glacier <d...@milanglacier.com> Commit: Milan Glacier <d...@milanglacier.com>
doc: update comment string. --- minuet.el | 140 ++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 95 insertions(+), 45 deletions(-) diff --git a/minuet.el b/minuet.el index e2c7b8b4d2..9d6e79a0f9 100644 --- a/minuet.el +++ b/minuet.el @@ -26,8 +26,9 @@ ;; Floor, Boston, MA 02110-1301, USA. ;;; Commentary: -;; This package implements an AI-powered code completion tool for Emacs. It -;; supports to use a variety of LLMs to generate code completions. +;; This package implements an AI-powered code completion tool for +;; Emacs. It supports to use a variety of LLMs to generate code +;; completions. ;;; Code: @@ -88,7 +89,8 @@ If any function in this list returns non-nil, auto-suggestions will not be shown (defvar-local minuet--debounce-timer nil "Timer for debouncing auto-suggestions.") -(defvar minuet-buffer-name "*minuet*" "The basename for minuet buffers") +(defvar minuet-buffer-name "*minuet*" "The basename for minuet buffers.") + (defcustom minuet-provider 'openai-fim-compatible "The provider to use for code completion. Must be one of the supported providers: codestral, openai, claude, etc." @@ -108,9 +110,9 @@ This limits how much surrounding code is sent to the LLM for context." (defcustom minuet-context-ratio 0.75 "Ratio of context before cursor vs after cursor. -When the total characters exceed the context window, this ratio determines -how much context to keep before vs after the cursor. A larger ratio means -more context before the cursor will be used." +When the total characters exceed the context window, this ratio +determines how much context to keep before vs after the cursor. A +larger ratio means more context before the cursor will be used." :type 'float :group 'minuet) @@ -129,21 +131,21 @@ completion item containing only its first line." (defcustom minuet-after-cursor-filter-length 15 "Length of context after cursor used to filter completion text. Defines the length of non-whitespace context after the cursor used to -filter completion text. Set to 0 to disable filtering. +filter completion text. Set to 0 to disable filtering. -Example: With after_cursor_filter_length = 3 and context: -'def fib(n):\\n|\\n\\nfib(5)' (where | represents cursor position), -if the completion text contains 'fib', then 'fib' and subsequent text -will be removed. This setting filters repeated text generated by the -LLM. A large value (e.g., 15) is recommended to avoid false positives." +Example: With after_cursor_filter_length = 3 and context: 'def +fib(n):\\n|\\n\\nfib(5)' (where | represents cursor position), if the +completion text contains 'fib', then 'fib' and subsequent text will be +removed. This setting filters repeated text generated by the LLM. A +large value (e.g., 15) is recommended to avoid false positives." :type 'integer :group 'minuet) (defcustom minuet-n-completions 3 "Number of completion items to request from the language model. -This number is encoded as part of the prompt for the chat LLM. Note that -when `minuet-add-single-line-entry' is true, the actual number of -returned items may exceed this value. Additionally, the LLM cannot +This number is encoded as part of the prompt for the chat LLM. Note +that when `minuet-add-single-line-entry' is true, the actual number of +returned items may exceed this value. Additionally, the LLM cannot guarantee the exact number of completion items specified, as this parameter serves only as a prompt guideline." :type 'integer @@ -161,7 +163,7 @@ enclosed in markers: Note that the user's code will be prompted in reverse order: first the code after the cursor, then the code before the cursor. " - "The default prompt for minuet completion") + "The default prompt for minuet completion.") (defvar minuet-default-guidelines "Guidelines: @@ -174,7 +176,7 @@ after the cursor, then the code before the cursor. additional comments or markdown code block fences. Return the result directly. 6. Keep each completion option concise, limiting it to a single line or a few lines. 7. Create entirely new code completion that DO NOT REPEAT OR COPY any user's existing code around <cursorPosition>." - "The default guidelines for minuet completion") + "The default guidelines for minuet completion.") (defvar minuet-default-n-completion-template "8. Provide at most %d completion items." @@ -182,7 +184,7 @@ after the cursor, then the code before the cursor. (defvar minuet-default-system-template "{{{:prompt}}}\n{{{:guidelines}}}\n{{{:n-completions-template}}}" - "The default template for minuet system template") + "The default template for minuet system template.") (defvar minuet-default-fewshots `((:role "user" @@ -221,7 +223,7 @@ def fibonacci(n): :n-completions-template minuet-default-n-completion-template) :fewshots minuet-default-fewshots :optional nil) - "config options for Minuet Claude provider") + "Config options for Minuet Claude provider.") (defvar minuet-openai-options `(:model "gpt-4o-mini" @@ -232,14 +234,14 @@ def fibonacci(n): :n-completions-template minuet-default-n-completion-template) :fewshots minuet-default-fewshots :optional nil) - "config options for Minuet OpenAI provider") + "Config options for Minuet OpenAI provider.") (defvar minuet-codestral-options '(:model "codestral-latest" :end-point "https://codestral.mistral.ai/v1/fim/completions" :api-key "CODESTRAL_API_KEY" :optional nil) - "config options for Minuet Codestral provider") + "Config options for Minuet Codestral provider.") (defvar minuet-openai-compatible-options `(:end-point "https://api.groq.com/openai/v1/chat/completions" @@ -252,7 +254,7 @@ def fibonacci(n): :n-completions-template minuet-default-n-completion-template) :fewshots minuet-default-fewshots :optional nil) - "config options for Minuet OpenAI compatible provider") + "Config options for Minuet OpenAI compatible provider.") (defvar minuet-openai-fim-compatible-options '(:model "deepseek-chat" @@ -260,7 +262,7 @@ def fibonacci(n): :api-key "DEEPSEEK_API_KEY" :name "Deepseek" :optional nil) - "config options for Minuet OpenAI FIM compatible provider") + "Config options for Minuet OpenAI FIM compatible provider.") (defvar minuet-gemini-options `(:model "gemini-1.5-flash-latest" @@ -275,20 +277,20 @@ def fibonacci(n): ;; (:stopSequences nil ;; :maxOutputTokens 256 ;; :topP 0.8)) - "config options for Minuet Gemini provider") + "Config options for Minuet Gemini provider.") (defun minuet-evil-not-insert-state-p () - "Return non-nil if evil is loaded and not in insert or emacs state." + "Return non-nil if evil is loaded and not in insert or Emacs state." (and (bound-and-true-p evil-local-mode) (not (or (evil-insert-state-p) (evil-emacs-state-p))))) (defun minuet-set-optional-options (options key val &optional field) - "Set the value of KEY in the FIELD of OPTIONS to VAL. If FIELD is -not provided, it defaults to :optional. If VAL is nil, then -remove KEY from OPTIONS. This helper function simplifies -setting values in a two-level nested plist structure." + "Set the value of KEY in the FIELD of OPTIONS to VAL. +If FIELD is not provided, it defaults to :optional. If VAL is nil, +then remove KEY from OPTIONS. This helper function simplifies setting +values in a two-level nested plist structure." (let ((field (or field :optional))) (if val (setf (plist-get options field) @@ -297,9 +299,10 @@ setting values in a two-level nested plist structure." (map-delete (plist-get options field) key))))) (defun minuet--eval-value (value) - "If value is a function (either lambda or a callable symbol), eval -the function (with no argument) and return the result. Else if value -is a symbol, return its value. Else return itself." + "Eval a VALUE for minuet. +If value is a function (either lambda or a callable symbol), eval the +function (with no argument) and return the result. Else if value is a +symbol, return its value. Else return itself." (cond ((functionp value) (funcall value)) ((boundp value) (symbol-value value)) (t value))) @@ -329,6 +332,7 @@ is a symbol, return its value. Else return itself." (not (eq minuet--last-point (point))))) (defun minuet--on-cursor-moved () + "Minuet event on cursor moved." (when (minuet--cursor-moved-p) (minuet--cleanup-suggestion))) @@ -401,7 +405,8 @@ is a symbol, return its value. Else return itself." (minuet--display-suggestion items 0))))))) (defun minuet--log (message &optional message-p) - "Log minuet messages into `minuet-buffer-name'. Also print the message when MESSAGE-P is t." + "Log minuet messages into `minuet-buffer-name'. +Also print the MESSAGE when MESSAGE-P is t." (with-current-buffer (get-buffer-create minuet-buffer-name) (goto-char (point-max)) (insert (format "%s %s\n" message (format-time-string "%Y-%02m-%02d %02H:%02M:%02S"))) @@ -409,6 +414,7 @@ is a symbol, return its value. Else return itself." (message "%s" message)))) (defun minuet--add-tab-comment () + "Add comment string for tab use into the prompt." (if-let* ((language-p (derived-mode-p 'prog-mode 'text-mode 'conf-mode)) (commentstring (format "%s %%s%s" (or (replace-regexp-in-string "^%" "%%" comment-start) "#") @@ -419,6 +425,7 @@ is a symbol, return its value. Else return itself." "")) (defun minuet--add-language-comment () + "Add comment string for language use into the prompt." (if-let* ((language-p (derived-mode-p 'prog-mode 'text-mode 'conf-mode)) (mode (symbol-name major-mode)) (mode (replace-regexp-in-string "-ts-mode" "" mode)) @@ -430,6 +437,7 @@ is a symbol, return its value. Else return itself." "")) (defun minuet--add-single-line-entry (data) + "Add single line entry into the DATA." (cl-loop for item in data when (stringp item) @@ -437,7 +445,7 @@ is a symbol, return its value. Else return itself." item))) (defun minuet--remove-spaces (items) - "Remove trailing and leading spaces in each item in items" + "Remove trailing and leading spaces in each item in ITEMS." ;; emacs use \\` and \\' to match the beginning/end of the string, ;; ^ and $ are used to match bol or eol (setq items (mapcar (lambda (x) @@ -451,6 +459,7 @@ is a symbol, return its value. Else return itself." items) (defun minuet--get-context () + "Get the context for minuet completion." (let* ((point (point)) (n-chars-before point) (point-max (point-max)) @@ -484,6 +493,7 @@ is a symbol, return its value. Else return itself." :additional ,(format "%s\n%s" (minuet--add-language-comment) (minuet--add-tab-comment))))) (defun minuet--make-chat-llm-shot (context) + "Build the prompt for chat llm from CONTEXT." (concat (plist-get context :additional) "\n<contextAfterCursor>\n" @@ -493,6 +503,7 @@ is a symbol, return its value. Else return itself." "<cursorPosition>")) (defun minuet--make-context-filter-sequence (context len) + "Create a filtering string based on CONTEXT with maximum length LEN." (if-let* ((is-string (stringp context)) (is-positive (> len 0)) (context (replace-regexp-in-string "\\`[\s\t\n]+" "" context)) @@ -526,6 +537,7 @@ is a symbol, return its value. Else return itself." minuet-after-cursor-filter-length))) (defun minuet--stream-decode (response get-text-fn) + "Decode the RESPONSE using GET-TEXT-FN." (setq response (split-string response "[\r]?\n")) (let (result) (dolist (line response) @@ -547,22 +559,24 @@ is a symbol, return its value. Else return itself." result))) (defmacro minuet--make-process-stream-filter (response) - "store the data into responses which is a plain list" + "Store the data into RESPONSE which is a plain list." `(lambda (proc text) (funcall #'internal-default-process-filter proc text) ;; (setq ,response (append ,response (list text))) (push text ,response))) (defun minuet--stream-decode-raw (response get-text-fn) - "Decode the raw stream stored in the temp variable create by `minuet--make-process-stream-filter'" + "Decode the raw stream stored in the temp variable create by `minuet--make-process-stream-filter' from RESPONSE and GET-TEXT-FN." (when-let* ((response (nreverse response)) (response (apply #'concat response))) (minuet--stream-decode response get-text-fn))) (defun minuet--handle-chat-completion-timeout (context err response get-text-fn name callback) - "Handle the timeout error for chat completion. This function will -decode and send the partial complete response to the callback,and log -the error" + "Handle the timeout error for chat completion. +This function will decode and send the partial complete response to +the callback, and log the error. CONTEXT, ERR, RESPONSE, GET-TEXT-FN, +NAME, CALLBACK are used to deliver partial completion items and log +the errors." (if (equal (car (plz-error-curl-error err)) 28) (progn (minuet--log (format "%s Request timeout" name)) @@ -578,11 +592,11 @@ the error" (defmacro minuet--with-temp-response (&rest body) "Execute BODY with a temporary response collection. -This macro creates a local variable `--response--' that can be used -to collect process output within the BODY. It's designed to work in -conjunction with `minuet--make-process-stream-filter'. -The `--response--' variable is initialized as an empty list and can -be used to accumulate text output from a process. After execution, +This macro creates a local variable `--response--' that can be used to +collect process output within the BODY. It's designed to work in +conjunction with `minuet--make-process-stream-filter'. The +`--response--' variable is initialized as an empty list and can be +used to accumulate text output from a process. After execution, `--response--' will contain the collected responses in reverse order." `(let (--response--) ,@body)) @@ -648,19 +662,24 @@ be used to accumulate text output from a process. After execution, (completion-in-region (point) (point) items)))))) (defun minuet--check-env-var (env-var) + "Check if ENV-VAR exists." (when-let ((var (getenv env-var))) (not (equal var "")))) (defun minuet--codestral-available-p () + "Check if codestral if available." (minuet--check-env-var (plist-get minuet-codestral-options :api-key))) (defun minuet--openai-available-p () + "Check if openai if available." (minuet--check-env-var "OPENAI_API_KEY")) (defun minuet--claude-available-p () + "Check if claude is available." (minuet--check-env-var "ANTHROPIC_API_KEY")) (defun minuet--openai-compatible-available-p () + "Check if the specified openai-compatible service is available." (when-let* ((options minuet-openai-compatible-options) (env-var (plist-get options :api-key)) (end-point (plist-get options :end-point)) @@ -668,6 +687,7 @@ be used to accumulate text output from a process. After execution, (minuet--check-env-var env-var))) (defun minuet--openai-fim-compatible-available-p () + "Check if the specified openai-fim-compatible service is available." (when-let* ((options minuet-openai-fim-compatible-options) (env-var (plist-get options :api-key)) (name (plist-get options :name)) @@ -676,12 +696,15 @@ be used to accumulate text output from a process. After execution, (minuet--check-env-var env-var))) (defun minuet--gemini-available-p () + "Check if gemini is available." (minuet--check-env-var "GEMINI_API_KEY")) (defun minuet--parse-completion-itmes-default (items) + "Parse ITEMS into a list of completion entries." (split-string items "<endCompletion>")) (defun minuet--make-system-prompt (template &optional n-completions) + "Create system prompt used in chat LLM from TEMPLATE and N-COMPLETIONS." (let* ((tmpl (plist-get template :template)) (tmpl (minuet--eval-value tmpl)) (n-completions (or n-completions minuet-n-completions 1)) @@ -712,6 +735,11 @@ be used to accumulate text output from a process. After execution, tmpl)) (defun minuet--openai-fim-complete-base (options get-text-fn context callback) + "The base function to complete code with openai fim API. +OPTIONS are the provider options. GET-TEXT-FN are the function to get +the completion items from json. CONTEXT is to be used to build the +prompt. CALLBACK is the function to be called when completion items +arrive." (let ((total-try (or minuet-n-completions 1)) (name (plist-get options :name)) completion-items) @@ -760,6 +788,8 @@ be used to accumulate text output from a process. After execution, minuet--current-requests))))) (defun minuet--codestral-complete (context callback) + "Complete code with codestral. +CONTEXT and CALLBACK will be passed to the base function." (minuet--openai-fim-complete-base (plist-put (copy-tree minuet-codestral-options) :name "Codestral") #'minuet--openai-get-text-fn @@ -767,6 +797,8 @@ be used to accumulate text output from a process. After execution, callback)) (defun minuet--openai-fim-compatible-complete (context callback) + "Complete code with openai fim API. +CONTEXT and CALLBACK will be passed to the base function." (minuet--openai-fim-complete-base (copy-tree minuet-openai-fim-compatible-options) #'minuet--openai-fim-get-text-fn @@ -774,12 +806,14 @@ be used to accumulate text output from a process. After execution, callback)) (defun minuet--openai-fim-get-text-fn (json) + "Function to get the completion from a JSON object for openai-fim compatible service." (--> json (plist-get it :choices) car (plist-get it :text))) (defun minuet--openai-get-text-fn (json) + "Function to get the completion from a JSON object for openai compatible service." (--> json (plist-get it :choices) car @@ -787,6 +821,10 @@ be used to accumulate text output from a process. After execution, (plist-get it :content))) (defun minuet--openai-complete-base (options context callback) + "The base function to complete code with openai API. +OPTIONS are the provider options. the completion items from json. +CONTEXT is to be used to build the prompt. CALLBACK is the function +to be called when completion items arrive." (minuet--with-temp-response (push (plz 'post (plist-get options :end-point) @@ -822,6 +860,8 @@ be used to accumulate text output from a process. After execution, minuet--current-requests))) (defun minuet--openai-complete (context callback) + "Complete code with OpenAI. +CONTEXT and CALLBACK will be passed to the base function." (minuet--openai-complete-base (--> (copy-tree minuet-openai-options) (plist-put it :end-point "https://api.openai.com/v1/chat/completions") @@ -829,15 +869,21 @@ be used to accumulate text output from a process. After execution, context callback)) (defun minuet--openai-compatible-complete (context callback) + "Complete code with OpenAI compatible service. +CONTEXT and CALLBACK will be passed to the base function." (minuet--openai-complete-base (copy-tree minuet-openai-compatible-options) context callback)) (defun minuet--claude-get-text-fn (json) + "Function to get the completion from a JSON object for claude." (--> json (plist-get it :delta) (plist-get it :text))) (defun minuet--claude-complete (context callback) + "Complete code with Claude. +CONTEXT is to be used to build the prompt. CALLBACK is the function +to be called when completion items arrive." (minuet--with-temp-response (push (plz 'post "https://api.anthropic.com/v1/messages" @@ -875,6 +921,7 @@ be used to accumulate text output from a process. After execution, minuet--current-requests))) (defun minuet--gemini-get-text-fn (json) + "Function to get the completion from a JSON object for gemini." (--> json (plist-get it :candidates) car @@ -884,6 +931,9 @@ be used to accumulate text output from a process. After execution, (plist-get it :text))) (defun minuet--gemini-complete (context callback) + "Complete code with gemini. +CONTEXT is to be used to build the prompt. CALLBACK is the function +to be called when completion items arrive." (minuet--with-temp-response (push (plz 'post (format "https://generativelanguage.googleapis.com/v1beta/models/%s:streamGenerateContent?alt=sse&key=%s" @@ -927,7 +977,7 @@ be used to accumulate text output from a process. After execution, (defun minuet--setup-auto-suggestion () - "Setup auto-suggestion with post-command-hook." + "Setup auto-suggestion with `post-command-hook'." (add-hook 'post-command-hook #'minuet--maybe-show-suggestion nil t)) (defun minuet--maybe-show-suggestion ()