From d7116388ab3a08ae189a788fbc84749262757c16 Mon Sep 17 00:00:00 2001 From: Samuel D Date: Mon, 25 Sep 2023 20:56:16 +0200 Subject: [PATCH 01/10] Proof of concept for a overlay completion system --- codegeex-api.el | 88 +++++++++++++++++ codegeex-completion.el | 51 ++++++++++ codegeex-overlay.el | 106 ++++++++++++++++++++ codegeex.el | 215 +++++++++++++++++++++++++++++++---------- 4 files changed, 410 insertions(+), 50 deletions(-) create mode 100644 codegeex-api.el create mode 100644 codegeex-completion.el create mode 100644 codegeex-overlay.el diff --git a/codegeex-api.el b/codegeex-api.el new file mode 100644 index 0000000..4661500 --- /dev/null +++ b/codegeex-api.el @@ -0,0 +1,88 @@ +;;; codegeex-api.el --- CodeGeeX For Emacs -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Samuel D + +;; Author: Samuel D +;; Keywords: codegeex, completion + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This is handling the api of codegeex + +;;; Code: + +;; COMPLETION + +(defun codegeex-api--get-completion (prefix suffix lang callback) + "Invoke CodeGeeX completion API. + +This function will complete code between PREFIX and SUFFIX, which are usually +the content before cursor and after cursor, and put the result to the current +buffer. LANG is the programming lanuauge of the code +CALLBACK is launched with the content of the buffer." + (let ((url (concat codegeex-endpoint "multilingual_code_generate_adapt")) + (data (codegeex-api--generate-json-data prefix suffix lang))) + (codegeex-api--url-retrieve url data callback))) + +(defun codegeex-api--url-retrieve (url json-data callback) + "Default url retrieve as POST with json data + +TODO : CALLBACK takes a json plist as argument" + (let ((url-request-method "POST") + (url-request-extra-headers + '(("Content-Type" . "application/json"))) + (url-request-data json-data)) + (url-retrieve + url + (lambda (status init-buffer callback) + (let ((result (codegeex-api--get-json-result))) + (with-current-buffer init-buffer + (funcall callback result)))) + `(,(current-buffer) ,callback) t))) + +(defun codegeex-api--get-json-result () + "Get the code string from the json response +TODO: Create a plist with the json response to avoid that +kind of shit" + (goto-char (point-min)) + (re-search-forward "^$") + (aref + (assoc-default + 'code + (assoc-default + 'output + (assoc-default + 'result + (json-read-from-string + (buffer-substring (point) (point-max)))))) + 0)) + +(defun codegeex-api--generate-json-data (prefix suffix lang) + "Create Json-encoded data to send to codegeex API" + (json-encode `((prompt . ,prefix) + (suffix . ,suffix) + (n . 1) + (apikey . ,codegeex-apikey) + (apisecret . ,codegeex-apisecret) + (temperature . ,codegeex-temperature) + (top_p . ,codegeex-top_p) + (top_k . ,codegeex-top_k) + (isFimEnabled . ,(not (equal suffix ""))) + (lang . ,lang) + (ext . ,codegeex-extinfo)))) + +(provide 'codegeex-api) +;;; codegeex-api.el ends here diff --git a/codegeex-completion.el b/codegeex-completion.el new file mode 100644 index 0000000..c46eea4 --- /dev/null +++ b/codegeex-completion.el @@ -0,0 +1,51 @@ +;;; codegeex-completion.el --- CodeGeeX For Emacs -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Samuel D + +;; Author: Samuel D +;; Keywords: codegeex, completion + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Heavily inspired by copilot.el + +;;; Code: + +(require 'codegeex-api) +(require 'codegeex-overlay) + +(defun codegeex-completion--show-completion (completion-string) + (save-excursion + (save-restriction + (widen) + (let* ((p (point)) + (start p) + (end (+ p (length completion-string)))) + (codegeex--display-overlay-completion + completion-string start end))))) + + +(defun codegeex-completion--get-completion (callback) + "Retrieve context (prefix and suffix) and language and invoke `codegeex-api--get-completion' +CALLBACK is launched with result of the call" + (let ((prefix (buffer-substring (point-min) (point))) + (suffix (buffer-substring (point) (point-max))) + (language (codegeex-language))) + (codegeex-api--get-completion + prefix suffix language callback))) + +(provide 'codegeex-completion) +;;; codegeex-completion.el ends here diff --git a/codegeex-overlay.el b/codegeex-overlay.el new file mode 100644 index 0000000..3f2e8e5 --- /dev/null +++ b/codegeex-overlay.el @@ -0,0 +1,106 @@ +;;; codegeex-overlay.el --- Codegeex for Emacs -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Samuel D + +;; Author: Samuel D +;; Keywords: convenience + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Taken from copilot.el +;; Create and handle the overlay shown during +;; buffer completion process + +;;; Code: + +(defface codegeex-overlay-face + '((t :inherit shadow)) + "Face for codegeex overlay.") + +(defvar-local codegeex--overlay nil + "Overlay for Codegeex completion.") + +(defvar-local codegeex--real-posn nil + "Posn information without overlay. +To work around posn problems with after-string property.") + +(defconst codegeex-overlay-completion-map (make-sparse-keymap) + "Keymap for Codegeex completion overlay.") + +(defun codegeex--get-overlay () + "Create or get overlay for Codegeex." + (unless (overlayp codegeex--overlay) + (setq codegeex--overlay (make-overlay 1 1 nil nil t)) + (overlay-put codegeex--overlay + 'keymap codegeex-overlay-completion-map) + (overlay-put codegeex--overlay 'priority 100)) + codegeex--overlay) + +(defun codegeex--overlay-end (ov) + "Return the end position of overlay OV." + (- (line-end-position) (overlay-get ov 'tail-length))) + +(defun codegeex--set-overlay-text (ov completion) + "Set overlay OV with COMPLETION." + (move-overlay ov (point) (line-end-position)) + (let* ((tail (buffer-substring (codegeex--overlay-end ov) (line-end-position))) + (p-completion (concat (propertize completion 'face 'codegeex-overlay-face) + tail))) + (if (eolp) + (progn + (overlay-put ov 'after-string "") ; make sure posn is correct + (setq codegeex--real-posn (cons (point) (posn-at-point))) + (put-text-property 0 1 'cursor t p-completion) + (overlay-put ov 'display "") + (overlay-put ov 'after-string p-completion)) + (overlay-put ov 'display (substring p-completion 0 1)) + (overlay-put ov 'after-string (substring p-completion 1))) + (overlay-put ov 'completion completion) + (overlay-put ov 'start (point)))) + +(defun codegeex--display-overlay-completion-1 (completion start end) + ;; Create overlay and set its text. + ) + +(defun codegeex--display-overlay-completion (completion start end) + "Show COMPLETION between START and END." + (setq end start) + (codegeex-clear-overlay) + (message "%s %d %d" completion start end) + (when (and (s-present-p completion) + (or (= start (point)) ; up-to-date completion + (and (< start (point)) ; special case for removing indentation + (s-blank-p (s-trim (buffer-substring-no-properties start (point))))))) + (goto-char start) ; indentation + (let ((ov (codegeex--get-overlay))) + (overlay-put ov 'tail-length (- (line-end-position) end)) + (codegeex--set-overlay-text ov completion)))) + +(defun codegeex-clear-overlay () + "Clear Codegeex overlay" + (interactive) + (when (codegeex--overlay-visible) + (delete-overlay codegeex--overlay) + (setq codegeex--real-posn nil))) + +(defsubst codegeex--overlay-visible () + "Return whether the `codegeex--overlay' is avaiable." + (and (overlayp codegeex--overlay) + (overlay-buffer codegeex--overlay))) + + +(provide 'codegeex-overlay) +;;; codegeex-overlay.el ends here diff --git a/codegeex.el b/codegeex.el index 007b0eb..247fef4 100644 --- a/codegeex.el +++ b/codegeex.el @@ -31,6 +31,20 @@ (require 'url) (require 'json) (require 'uuidgen) +(require 'codegeex-completion) +(require 'codegeex-overlay) + + +(defcustom codegeex-idle-delay 0.5 + "Time in seconds to wait before starting completion. Complete immediately if set to 0." + :type 'float + :group 'codegeex) + +(defcustom codegeex-clear-overlay-ignore-commands nil + "List of commands that should not clear the overlay when called." + :group 'codegeex + :type '(repeat function)) + (defvar codegeex-endpoint "https://tianqi.aminer.cn/api/v2/" "the endpoint of CodeGeeX API") (defvar codegeex-apikey "68cf004321e94b47a91c2e45a8109852" "API key obtained from CodeGeeX website") @@ -40,47 +54,8 @@ (defvar codegeex-top_k 0 "top_k for completion by CodeGeeX") (defvar codegeex-extinfo `((sid . ,(uuidgen-4)) (ide . "Emacs") - (ideVersion . ,emacs-version)) "The ext field in JSON to be sent to server") - -(defun codegeex-completion-invoke (prefix suffix lang) - "Invoke CodeGeeX completion API. - -This function will complete code between PREFIX and SUFFIX, which are usually -the content before cursor and after cursor, and put the result to the current -buffer. LANG is the programming lanuauge of the code." - (let* ((url (concat codegeex-endpoint "multilingual_code_generate_adapt")) - (data (json-encode `((prompt . ,prefix) - (suffix . ,suffix) - (n . 1) - (apikey . ,codegeex-apikey) - (apisecret . ,codegeex-apisecret) - (temperature . ,codegeex-temperature) - (top_p . ,codegeex-top_p) - (top_k . ,codegeex-top_k) - (isFimEnabled . ,(not (equal suffix ""))) - (lang . ,lang) - (ext . ,codegeex-extinfo)))) - (url-request-method "POST") - (url-request-extra-headers - '(("Content-Type" . "application/json"))) - (url-request-data data)) - (url-retrieve - url - (lambda (status parent-buffer) - (goto-char (point-min)) - (re-search-forward "^$") - (delete-region (point) (point-min)) - (let* ((json-string (buffer-string)) - (json-data (json-read-from-string json-string)) - (json-result (assoc-default 'result json-data)) - (json-output (assoc-default 'output json-result)) - (json-code (assoc-default 'code json-output)) - (result (aref json-code 0))) - (with-current-buffer parent-buffer - (insert result)) - (kill-buffer))) - `(,(current-buffer)))) - nil) + (ideVersion . ,emacs-version)) + "The ext field in JSON to be sent to server") (defun codegeex-debug-invoke (prompt lang begin end) "Invoke the codegeex debugger API. @@ -126,16 +101,38 @@ buffer." name-without-mode)) ;;;###autoload -(defun codegeex-buffer-completion () - "CodeGeeX buffer completion. +(defun codegeex-complete () + "Get completion at point" + (interactive) + (codegeex-completion--get-completion + (lambda (completion) + (if completion + (codegeex-completion--show-completion completion) + (message "No completion available"))))) -The completion result will be put in the position of cursor directly." +;;;###autoload +(defun codegeex-accept-completion (&optional transform-fn) + "Accept completion. Return t if there is a completion. +Use TRANSFORM-FN to transform completion if provided." (interactive) - (message "CodeGeeX completing") - (let ((prefix (buffer-substring (point-min) (point))) - (suffix (buffer-substring (point) (point-max)))) - (codegeex-completion-invoke prefix suffix (codegeex-language))) - nil) + (when (codegeex--overlay-visible) + (let* ((completion (overlay-get codegeex--overlay 'completion)) + (start (overlay-get codegeex--overlay 'start)) + (end (codegeex--overlay-end codegeex--overlay)) + (uuid (overlay-get codegeex--overlay 'uuid)) + (t-completion (funcall (or transform-fn #'identity) completion))) + (codegeex-clear-overlay) + (if (eq major-mode 'vterm-mode) + (progn + (vterm-delete-region start end) + (vterm-insert t-completion)) + (delete-region start end) + (insert t-completion)) + ;; if it is a partial completion + (when (and (s-prefix-p t-completion completion) + (not (s-equals-p t-completion completion))) + (codegeex--set-overlay-text (codegeex--get-overlay) (s-chop-prefix t-completion completion))) + t))) ;;;###autoload (defun codegeex-buffer-debug () @@ -163,8 +160,126 @@ The result will be replaced into the selected region." (codegeex-debug-invoke prompt (codegeex-language) begin end)) nil) +;; minor mode + +(defvar codegeex--post-command-timer nil) + +(defcustom codegeex-disable-predicates nil + "A list of predicate functions with no argument to disable Codegeex. +Codegeex will not be triggered if any predicate returns t." + :type '(repeat function) + :group 'codegeex) + +(defcustom codegeex-enable-predicates '(evil-insert-state-p codegeex--buffer-changed) + "A list of predicate functions with no argument to enable Codegeex. +Codegeex will be triggered only if all predicates return t." + :type '(repeat function) + :group 'codegeex) + +(defcustom codegeex-disable-display-predicates nil + "A list of predicate functions with no argument to disable Codegeex. +Codegeex will not show completions if any predicate returns t." + :type '(repeat function) + :group 'codegeex) + +(defcustom codegeex-enable-display-predicates nil + "A list of predicate functions with no argument to enable Codegeex. +Codegeex will show completions only if all predicates return t." + :type '(repeat function) + :group 'codegeex) + +(defmacro codegeex--satisfy-predicates (enable disable) + "Return t if satisfy all predicates in ENABLE and none in DISABLE." + `(and (cl-every (lambda (pred) + (if (functionp pred) (funcall pred) t)) + ,enable) + (cl-notany (lambda (pred) + (if (functionp pred) (funcall pred) nil)) + ,disable))) + +(defun codegeex--satisfy-trigger-predicates () + "Return t if all trigger predicates are satisfied." + (codegeex--satisfy-predicates codegeex-enable-predicates codegeex-disable-predicates)) + +(defun codegeex--satisfy-display-predicates () + "Return t if all display predicates are satisfied." + (codegeex--satisfy-predicates codegeex-enable-display-predicates codegeex-disable-display-predicates)) + +(defvar codegeex-mode-map (make-sparse-keymap) + "Keymap for Codegeex minor mode. +Use this for custom bindings in `codegeex-mode'.") + +(defun codegeex--mode-enter () + "Set up codegeex mode when entering." + (add-hook 'post-command-hook #'codegeex--post-command nil 'local)) + +(defun codegeex--mode-exit () + "Clean up codegeex mode when exiting." + (remove-hook 'post-command-hook #'codegeex--post-command 'local)) + ;;;###autoload -(define-key global-map (kbd "M-\\") 'codegeex-buffer-completion) +(define-minor-mode codegeex-mode + "Minor mode for Codegeex." + :init-value nil + :lighter " Codegeex" + (codegeex-clear-overlay) + (advice-add 'posn-at-point :before-until #'codegeex--posn-advice) + (if codegeex-mode + (codegeex--mode-enter) + (codegeex--mode-exit))) + +(defun codegeex--posn-advice (&rest args) + "Remap posn if in codegeex-mode." + (when codegeex-mode + (let ((pos (or (car-safe args) (point)))) + (when (and codegeex--real-posn + (eq pos (car codegeex--real-posn))) + (cdr codegeex--real-posn))))) + +;;;###autoload +(define-global-minor-mode global-codegeex-mode + codegeex-mode codegeex-mode) + +(defun codegeex--post-command () + "Complete in `post-command-hook' hook." + (when (and this-command + (not (and (symbolp this-command) + (or + (s-starts-with-p "codegeex-" (symbol-name this-command)) + (member this-command codegeex-clear-overlay-ignore-commands) + (codegeex--self-insert this-command))))) + (codegeex-clear-overlay) + (when codegeex--post-command-timer + (cancel-timer codegeex--post-command-timer)) + (setq codegeex--post-command-timer + (run-with-idle-timer codegeex-idle-delay + nil + #'codegeex--post-command-debounce + (current-buffer))))) + +(defun codegeex--self-insert (command) + "Handle the case where the char just inserted is the start of the completion. +If so, update the overlays and continue. COMMAND is the +command that triggered `post-command-hook'." + (when (and (eq command 'self-insert-command) + (codegeex--overlay-visible) + (codegeex--satisfy-display-predicates)) + (let* ((ov codegeex--overlay) + (completion (overlay-get ov 'completion))) + ;; The char just inserted is the next char of completion + (when (eq last-command-event (elt completion 0)) + (if (= (length completion) 1) + ;; If there is only one char in the completion, accept it + (codegeex-accept-completion) + (codegeex--set-overlay-text ov (substring completion 1))))))) + +(defun codegeex--post-command-debounce (buffer) + "Complete in BUFFER." + (when (and (buffer-live-p buffer) + (equal (current-buffer) buffer) + codegeex-mode + (codegeex--satisfy-trigger-predicates)) + (codegeex-complete))) (provide 'codegeex) ;;; codegeex.el ends here From 88629e70dcd16d16307b6edefdfef4b55b266631 Mon Sep 17 00:00:00 2001 From: Samuel D Date: Wed, 27 Sep 2023 19:23:52 +0200 Subject: [PATCH 02/10] Working version --- codegeex-api.el | 54 ++++++++++----------- codegeex-completion.el | 9 ++-- codegeex-overlay.el | 5 -- codegeex.el | 108 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 127 insertions(+), 49 deletions(-) diff --git a/codegeex-api.el b/codegeex-api.el index 4661500..f754b66 100644 --- a/codegeex-api.el +++ b/codegeex-api.el @@ -38,9 +38,7 @@ CALLBACK is launched with the content of the buffer." (codegeex-api--url-retrieve url data callback))) (defun codegeex-api--url-retrieve (url json-data callback) - "Default url retrieve as POST with json data - -TODO : CALLBACK takes a json plist as argument" + "Default url retrieve as POST with json data" (let ((url-request-method "POST") (url-request-extra-headers '(("Content-Type" . "application/json"))) @@ -49,40 +47,42 @@ TODO : CALLBACK takes a json plist as argument" url (lambda (status init-buffer callback) (let ((result (codegeex-api--get-json-result))) + (setq codegeex-response-cache result) (with-current-buffer init-buffer (funcall callback result)))) `(,(current-buffer) ,callback) t))) (defun codegeex-api--get-json-result () - "Get the code string from the json response -TODO: Create a plist with the json response to avoid that -kind of shit" + "Get the code string from the json response" (goto-char (point-min)) (re-search-forward "^$") - (aref - (assoc-default - 'code - (assoc-default - 'output - (assoc-default - 'result - (json-read-from-string - (buffer-substring (point) (point-max)))))) - 0)) + (let ((json-string (buffer-substring (point) (point-max)))) + (setq codegeex-json-string-cache json-string) + (json-parse-string json-string + :object-type 'plist + :array-type 'list))) (defun codegeex-api--generate-json-data (prefix suffix lang) "Create Json-encoded data to send to codegeex API" - (json-encode `((prompt . ,prefix) - (suffix . ,suffix) - (n . 1) - (apikey . ,codegeex-apikey) - (apisecret . ,codegeex-apisecret) - (temperature . ,codegeex-temperature) - (top_p . ,codegeex-top_p) - (top_k . ,codegeex-top_k) - (isFimEnabled . ,(not (equal suffix ""))) - (lang . ,lang) - (ext . ,codegeex-extinfo)))) + (let ((n-factor + (cond + ((<= (length prefix) 300) 3) + ((> (length prefix) 600) 2) + ((> (length prefix) 900) 1)))) + (when (> (length prefix) 1200 ) + (setq prefix (substring prefix 0 1200))) + (json-encode + `(:prompt ,prefix + :suffix ,suffix + :n ,n-factor + :apikey ,codegeex-apikey + :apisecret ,codegeex-apisecret + :temperature ,codegeex-temperature + :top_p ,codegeex-top_p + :top_k ,codegeex-top_k + :isFimEnabled ,(not (equal suffix "")) + :lang ,lang + :ext ,codegeex-extinfo)))) (provide 'codegeex-api) ;;; codegeex-api.el ends here diff --git a/codegeex-completion.el b/codegeex-completion.el index c46eea4..efbaba1 100644 --- a/codegeex-completion.el +++ b/codegeex-completion.el @@ -27,20 +27,19 @@ (require 'codegeex-api) (require 'codegeex-overlay) -(defun codegeex-completion--show-completion (completion-string) +(defun codegeex-completion--show-completion (completion) (save-excursion (save-restriction (widen) (let* ((p (point)) (start p) - (end (+ p (length completion-string)))) + (end (+ p (length completion)))) (codegeex--display-overlay-completion - completion-string start end))))) - + completion start end))))) (defun codegeex-completion--get-completion (callback) "Retrieve context (prefix and suffix) and language and invoke `codegeex-api--get-completion' -CALLBACK is launched with result of the call" +CALLBACK is launched with json result of the call" (let ((prefix (buffer-substring (point-min) (point))) (suffix (buffer-substring (point) (point-max))) (language (codegeex-language))) diff --git a/codegeex-overlay.el b/codegeex-overlay.el index 3f2e8e5..c452fa2 100644 --- a/codegeex-overlay.el +++ b/codegeex-overlay.el @@ -71,15 +71,10 @@ To work around posn problems with after-string property.") (overlay-put ov 'completion completion) (overlay-put ov 'start (point)))) -(defun codegeex--display-overlay-completion-1 (completion start end) - ;; Create overlay and set its text. - ) - (defun codegeex--display-overlay-completion (completion start end) "Show COMPLETION between START and END." (setq end start) (codegeex-clear-overlay) - (message "%s %d %d" completion start end) (when (and (s-present-p completion) (or (= start (point)) ; up-to-date completion (and (< start (point)) ; special case for removing indentation diff --git a/codegeex.el b/codegeex.el index 247fef4..c5ca12d 100644 --- a/codegeex.el +++ b/codegeex.el @@ -3,8 +3,9 @@ ;; Copyright (C) 2023 Hao Zhang ;; Author: Hao Zhang +;; Contributor: Samuel Dawant ;; Keywords: codegeex, completion -;; Version: 0.0.1 +;; Version: 0.1.0 ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -35,6 +36,12 @@ (require 'codegeex-overlay) +(defvar codegeex-json-string-cache nil + "Mainly for debugging but maybe will be useful") + +(defvar codegeex-response-cache nil + "Mainly for debugging but maybe will be useful") + (defcustom codegeex-idle-delay 0.5 "Time in seconds to wait before starting completion. Complete immediately if set to 0." :type 'float @@ -45,13 +52,24 @@ :group 'codegeex :type '(repeat function)) +(defvar codegeex-endpoint "https://tianqi.aminer.cn/api/v2/" + "the endpoint of CodeGeeX API") + +(defvar codegeex-apikey "68cf004321e94b47a91c2e45a8109852" + "API key obtained from CodeGeeX website") + +(defvar codegeex-apisecret "e82b86a16f9d471ab215f653060310e3" + "API secret obtained from CodeGeeX website") + +(defvar codegeex-temperature 0.2 + "temperature for completion by CodeGeeX") + +(defvar codegeex-top_p 0.95 + "top_p for completion by CodeGeeX") + +(defvar codegeex-top_k 0 + "top_k for completion by CodeGeeX") -(defvar codegeex-endpoint "https://tianqi.aminer.cn/api/v2/" "the endpoint of CodeGeeX API") -(defvar codegeex-apikey "68cf004321e94b47a91c2e45a8109852" "API key obtained from CodeGeeX website") -(defvar codegeex-apisecret "e82b86a16f9d471ab215f653060310e3" "API secret obtained from CodeGeeX website") -(defvar codegeex-temperature 0.2 "temperature for completion by CodeGeeX") -(defvar codegeex-top_p 0.95 "top_p for completion by CodeGeeX") -(defvar codegeex-top_k 0 "top_k for completion by CodeGeeX") (defvar codegeex-extinfo `((sid . ,(uuidgen-4)) (ide . "Emacs") (ideVersion . ,emacs-version)) @@ -100,15 +118,56 @@ buffer." (name-without-mode (replace-regexp-in-string "-mode" "" name))) name-without-mode)) +(defun codegeex--plist-get (plist &rest keys) + "Browse PLIST object for each KEYS and return the element +where it stop. +KEYS can be either numbers or properties symbols" + (let ((res json-result)) + (dolist (key keys) + (cond + ((symbolp key) + (setq res (plist-get res key))) + ((numberp key) + (setq res (nth key res))) + (t + (error "Key '%s' format not supported" key)))) + res)) + +(defvar-local codegeex--completion-cache nil) +(defvar-local codegeex--completion-idx 0) + ;;;###autoload (defun codegeex-complete () - "Get completion at point" + "Get completion at point, showing the completion in an overlay" + (interactive) + (setq codegeex--completion-cache nil) + (codegeex-completion--get-completion + (lambda (json-result) + (let* ((completions + (cl-remove-if + (lambda (e) + (string= "" (string-trim e))) + (cl-remove-duplicates + (codegeex--plist-get + json-result :result :output :code)))) + (completion (if (seq-empty-p completions) nil (seq-elt completions 0)))) + (setq codegeex--completion-cache completions) + (if completion + (codegeex-completion--show-completion completion) + (message "No completion available")))))) + +;;;###autoload +(defun codegeex-complete-at-point () + "Get first completion proposed and insert it at point +Can be used without having to be in `codegeex-mode'" (interactive) (codegeex-completion--get-completion - (lambda (completion) - (if completion - (codegeex-completion--show-completion completion) - (message "No completion available"))))) + (lambda (json-result) + (let ((completion (codegeex--plist-get + json-result :result :output :code 0))) + (if completion + (insert completion) + (message "No completion available")))))) ;;;###autoload (defun codegeex-accept-completion (&optional transform-fn) @@ -134,6 +193,31 @@ Use TRANSFORM-FN to transform completion if provided." (codegeex--set-overlay-text (codegeex--get-overlay) (s-chop-prefix t-completion completion))) t))) +(defun codegeex--cycle-completion (direction) + "Cycle completion with DIRECTION." + (let ((completions codegeex--completion-cache)) + (cond ((seq-empty-p completions) + (message "No completion is available.")) + ((= (length completions) 1) + (message "Only one completion is available.")) + (t (let ((idx (mod (+ codegeex--completion-idx direction) + (length completions)))) + (setq codegeex--completion-idx idx) + (let ((completion (elt completions idx))) + (codegeex-completion--show-completion completion))))))) + +(defun codegeex-next-completion () + "Cycle to next completion." + (interactive) + (when (codegeex--overlay-visible) + (codegeex--cycle-completion 1))) + +(defun codegeex-previous-completion () + "Cycle to previous completion." + (interactive) + (when (codegeex--overlay-visible) + (codegeex--cycle-completion -1))) + ;;;###autoload (defun codegeex-buffer-debug () "CodeGeeX debugging for the current buffer. From 161bb2b0d5eec883a090b5bc371861d4c6fb30f7 Mon Sep 17 00:00:00 2001 From: Samuel D Date: Wed, 27 Sep 2023 22:01:04 +0200 Subject: [PATCH 03/10] Typo --- codegeex.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegeex.el b/codegeex.el index c5ca12d..a8a536f 100644 --- a/codegeex.el +++ b/codegeex.el @@ -122,7 +122,7 @@ buffer." "Browse PLIST object for each KEYS and return the element where it stop. KEYS can be either numbers or properties symbols" - (let ((res json-result)) + (let ((res plist)) (dolist (key keys) (cond ((symbolp key) From ecf755aa06eee45bae0ce521e8e904ba055e12ad Mon Sep 17 00:00:00 2001 From: Samuel D Date: Sun, 1 Oct 2023 00:19:17 +0200 Subject: [PATCH 04/10] Fixed: prefix/prompt substring was trunacting the end of the string + Increase prompt length limit to 5000 characters --- codegeex-api.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codegeex-api.el b/codegeex-api.el index f754b66..827a7fc 100644 --- a/codegeex-api.el +++ b/codegeex-api.el @@ -69,8 +69,8 @@ CALLBACK is launched with the content of the buffer." ((<= (length prefix) 300) 3) ((> (length prefix) 600) 2) ((> (length prefix) 900) 1)))) - (when (> (length prefix) 1200 ) - (setq prefix (substring prefix 0 1200))) + (when (> (length prefix) 5000 ) + (setq prefix (substring prefix -5000))) (json-encode `(:prompt ,prefix :suffix ,suffix From 43585ccc3f97d419d3fc240025a6d63eedda49dd Mon Sep 17 00:00:00 2001 From: Samuel D Date: Sun, 1 Oct 2023 00:20:22 +0200 Subject: [PATCH 05/10] Added: Cache the json request --- codegeex-api.el | 1 + codegeex.el | 3 +++ 2 files changed, 4 insertions(+) diff --git a/codegeex-api.el b/codegeex-api.el index 827a7fc..493f14d 100644 --- a/codegeex-api.el +++ b/codegeex-api.el @@ -43,6 +43,7 @@ CALLBACK is launched with the content of the buffer." (url-request-extra-headers '(("Content-Type" . "application/json"))) (url-request-data json-data)) + (setq codegeex-request-cache json-data) (url-retrieve url (lambda (status init-buffer callback) diff --git a/codegeex.el b/codegeex.el index a8a536f..fb81009 100644 --- a/codegeex.el +++ b/codegeex.el @@ -39,6 +39,9 @@ (defvar codegeex-json-string-cache nil "Mainly for debugging but maybe will be useful") +(defvar codegeex-request-cache nil + "Mainly for debugging but maybe will be useful") + (defvar codegeex-response-cache nil "Mainly for debugging but maybe will be useful") From 5ecc7b67f06036384c4658dd4e3176882b369414 Mon Sep 17 00:00:00 2001 From: Samuel D Date: Sun, 1 Oct 2023 00:21:12 +0200 Subject: [PATCH 06/10] Change defun order --- codegeex.el | 106 ++++++++++++++++++++++++++-------------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/codegeex.el b/codegeex.el index fb81009..0f69aad 100644 --- a/codegeex.el +++ b/codegeex.el @@ -159,43 +159,6 @@ KEYS can be either numbers or properties symbols" (codegeex-completion--show-completion completion) (message "No completion available")))))) -;;;###autoload -(defun codegeex-complete-at-point () - "Get first completion proposed and insert it at point -Can be used without having to be in `codegeex-mode'" - (interactive) - (codegeex-completion--get-completion - (lambda (json-result) - (let ((completion (codegeex--plist-get - json-result :result :output :code 0))) - (if completion - (insert completion) - (message "No completion available")))))) - -;;;###autoload -(defun codegeex-accept-completion (&optional transform-fn) - "Accept completion. Return t if there is a completion. -Use TRANSFORM-FN to transform completion if provided." - (interactive) - (when (codegeex--overlay-visible) - (let* ((completion (overlay-get codegeex--overlay 'completion)) - (start (overlay-get codegeex--overlay 'start)) - (end (codegeex--overlay-end codegeex--overlay)) - (uuid (overlay-get codegeex--overlay 'uuid)) - (t-completion (funcall (or transform-fn #'identity) completion))) - (codegeex-clear-overlay) - (if (eq major-mode 'vterm-mode) - (progn - (vterm-delete-region start end) - (vterm-insert t-completion)) - (delete-region start end) - (insert t-completion)) - ;; if it is a partial completion - (when (and (s-prefix-p t-completion completion) - (not (s-equals-p t-completion completion))) - (codegeex--set-overlay-text (codegeex--get-overlay) (s-chop-prefix t-completion completion))) - t))) - (defun codegeex--cycle-completion (direction) "Cycle completion with DIRECTION." (let ((completions codegeex--completion-cache)) @@ -209,12 +172,14 @@ Use TRANSFORM-FN to transform completion if provided." (let ((completion (elt completions idx))) (codegeex-completion--show-completion completion))))))) +;;;###autoload (defun codegeex-next-completion () "Cycle to next completion." (interactive) (when (codegeex--overlay-visible) (codegeex--cycle-completion 1))) +;;;###autoload (defun codegeex-previous-completion () "Cycle to previous completion." (interactive) @@ -247,6 +212,43 @@ The result will be replaced into the selected region." (codegeex-debug-invoke prompt (codegeex-language) begin end)) nil) +;;;###autoload +(defun codegeex-complete-at-point () + "Get first completion proposed and insert it at point +Can be used without having to be in `codegeex-mode'" + (interactive) + (codegeex-completion--get-completion + (lambda (json-result) + (let ((completion (codegeex--plist-get + json-result :result :output :code 0))) + (if completion + (insert completion) + (message "No completion available")))))) + +;;;###autoload +(defun codegeex-accept-completion (&optional transform-fn) + "Accept completion. Return t if there is a completion. +Use TRANSFORM-FN to transform completion if provided." + (interactive) + (when (codegeex--overlay-visible) + (let* ((completion (overlay-get codegeex--overlay 'completion)) + (start (overlay-get codegeex--overlay 'start)) + (end (codegeex--overlay-end codegeex--overlay)) + (uuid (overlay-get codegeex--overlay 'uuid)) + (t-completion (funcall (or transform-fn #'identity) completion))) + (codegeex-clear-overlay) + (if (eq major-mode 'vterm-mode) + (progn + (vterm-delete-region start end) + (vterm-insert t-completion)) + (delete-region start end) + (insert t-completion)) + ;; if it is a partial completion + (when (and (s-prefix-p t-completion completion) + (not (s-equals-p t-completion completion))) + (codegeex--set-overlay-text (codegeex--get-overlay) (s-chop-prefix t-completion completion))) + t))) + ;; minor mode (defvar codegeex--post-command-timer nil) @@ -304,17 +306,6 @@ Use this for custom bindings in `codegeex-mode'.") "Clean up codegeex mode when exiting." (remove-hook 'post-command-hook #'codegeex--post-command 'local)) -;;;###autoload -(define-minor-mode codegeex-mode - "Minor mode for Codegeex." - :init-value nil - :lighter " Codegeex" - (codegeex-clear-overlay) - (advice-add 'posn-at-point :before-until #'codegeex--posn-advice) - (if codegeex-mode - (codegeex--mode-enter) - (codegeex--mode-exit))) - (defun codegeex--posn-advice (&rest args) "Remap posn if in codegeex-mode." (when codegeex-mode @@ -323,10 +314,6 @@ Use this for custom bindings in `codegeex-mode'.") (eq pos (car codegeex--real-posn))) (cdr codegeex--real-posn))))) -;;;###autoload -(define-global-minor-mode global-codegeex-mode - codegeex-mode codegeex-mode) - (defun codegeex--post-command () "Complete in `post-command-hook' hook." (when (and this-command @@ -368,5 +355,20 @@ command that triggered `post-command-hook'." (codegeex--satisfy-trigger-predicates)) (codegeex-complete))) +;;;###autoload +(define-global-minor-mode global-codegeex-mode + codegeex-mode codegeex-mode) + +;;;###autoload +(define-minor-mode codegeex-mode + "Minor mode for Codegeex." + :init-value nil + :lighter " Codegeex" + (codegeex-clear-overlay) + (advice-add 'posn-at-point :before-until #'codegeex--posn-advice) + (if codegeex-mode + (codegeex--mode-enter) + (codegeex--mode-exit))) + (provide 'codegeex) ;;; codegeex.el ends here From 4fd09370c3fede123af1dfff47fb238348965275 Mon Sep 17 00:00:00 2001 From: Samuel D Date: Sat, 14 Oct 2023 18:45:05 +0200 Subject: [PATCH 07/10] Merge codegeex-completion with main codegeex.el --- codegeex-completion.el | 50 ------------------------------------------ codegeex.el | 20 +++++++++++++++++ 2 files changed, 20 insertions(+), 50 deletions(-) delete mode 100644 codegeex-completion.el diff --git a/codegeex-completion.el b/codegeex-completion.el deleted file mode 100644 index efbaba1..0000000 --- a/codegeex-completion.el +++ /dev/null @@ -1,50 +0,0 @@ -;;; codegeex-completion.el --- CodeGeeX For Emacs -*- lexical-binding: t; -*- - -;; Copyright (C) 2023 Samuel D - -;; Author: Samuel D -;; Keywords: codegeex, completion - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; Heavily inspired by copilot.el - -;;; Code: - -(require 'codegeex-api) -(require 'codegeex-overlay) - -(defun codegeex-completion--show-completion (completion) - (save-excursion - (save-restriction - (widen) - (let* ((p (point)) - (start p) - (end (+ p (length completion)))) - (codegeex--display-overlay-completion - completion start end))))) - -(defun codegeex-completion--get-completion (callback) - "Retrieve context (prefix and suffix) and language and invoke `codegeex-api--get-completion' -CALLBACK is launched with json result of the call" - (let ((prefix (buffer-substring (point-min) (point))) - (suffix (buffer-substring (point) (point-max))) - (language (codegeex-language))) - (codegeex-api--get-completion - prefix suffix language callback))) - -(provide 'codegeex-completion) -;;; codegeex-completion.el ends here diff --git a/codegeex.el b/codegeex.el index 0f69aad..8786af1 100644 --- a/codegeex.el +++ b/codegeex.el @@ -33,6 +33,7 @@ (require 'json) (require 'uuidgen) (require 'codegeex-completion) +(require 'codegeex-api) (require 'codegeex-overlay) @@ -139,6 +140,25 @@ KEYS can be either numbers or properties symbols" (defvar-local codegeex--completion-cache nil) (defvar-local codegeex--completion-idx 0) +(defun codegeex-completion--show-completion (completion) + (save-excursion + (save-restriction + (widen) + (let* ((p (point)) + (start p) + (end (+ p (length completion)))) + (codegeex--display-overlay-completion + completion start end))))) + +(defun codegeex-completion--get-completion (callback) + "Retrieve context (prefix and suffix) and language and invoke `codegeex-api--get-completion' +CALLBACK is launched with json result of the call" + (let ((prefix (buffer-substring (point-min) (point))) + (suffix (buffer-substring (point) (point-max))) + (language (codegeex-language))) + (codegeex-api--get-completion + prefix suffix language callback))) + ;;;###autoload (defun codegeex-complete () "Get completion at point, showing the completion in an overlay" From 609e5b7afa13721c3d7d04b47ee9c8d244716885 Mon Sep 17 00:00:00 2001 From: Samuel D Date: Sat, 14 Oct 2023 18:45:16 +0200 Subject: [PATCH 08/10] check if cursor doesn't change between the http request and response --- codegeex.el | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/codegeex.el b/codegeex.el index 8786af1..39dcff1 100644 --- a/codegeex.el +++ b/codegeex.el @@ -139,6 +139,7 @@ KEYS can be either numbers or properties symbols" (defvar-local codegeex--completion-cache nil) (defvar-local codegeex--completion-idx 0) +(defvar-local codegeex--last-pos nil) (defun codegeex-completion--show-completion (completion) (save-excursion @@ -164,20 +165,22 @@ CALLBACK is launched with json result of the call" "Get completion at point, showing the completion in an overlay" (interactive) (setq codegeex--completion-cache nil) + (setq codegeex--last-pos (point)) (codegeex-completion--get-completion (lambda (json-result) - (let* ((completions - (cl-remove-if - (lambda (e) - (string= "" (string-trim e))) - (cl-remove-duplicates - (codegeex--plist-get - json-result :result :output :code)))) - (completion (if (seq-empty-p completions) nil (seq-elt completions 0)))) - (setq codegeex--completion-cache completions) - (if completion - (codegeex-completion--show-completion completion) - (message "No completion available")))))) + (when (equal (point) codegeex--last-pos) + (let* ((completions + (cl-remove-if + (lambda (e) + (string= "" (string-trim e))) + (cl-remove-duplicates + (codegeex--plist-get + json-result :result :output :code)))) + (completion (if (seq-empty-p completions) nil (seq-elt completions 0)))) + (setq codegeex--completion-cache completions) + (if completion + (codegeex-completion--show-completion completion) + (message "No completion available"))))))) (defun codegeex--cycle-completion (direction) "Cycle completion with DIRECTION." From d8a51f81b1eb5e0c3c7ab477b6c309a2ff28bef9 Mon Sep 17 00:00:00 2001 From: Samuel D Date: Sun, 15 Oct 2023 14:37:42 +0200 Subject: [PATCH 09/10] REmove require statement --- codegeex.el | 1 - 1 file changed, 1 deletion(-) diff --git a/codegeex.el b/codegeex.el index 39dcff1..e41a30f 100644 --- a/codegeex.el +++ b/codegeex.el @@ -32,7 +32,6 @@ (require 'url) (require 'json) (require 'uuidgen) -(require 'codegeex-completion) (require 'codegeex-api) (require 'codegeex-overlay) From d03c5518c3d304ce5eebcb845d67d1f4d4245af8 Mon Sep 17 00:00:00 2001 From: Samuel D Date: Fri, 20 Oct 2023 22:26:20 +0200 Subject: [PATCH 10/10] Add missing requirement --- codegeex.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegeex.el b/codegeex.el index e41a30f..6f7b5e9 100644 --- a/codegeex.el +++ b/codegeex.el @@ -29,13 +29,13 @@ ;;; Code: +(require 's) (require 'url) (require 'json) (require 'uuidgen) (require 'codegeex-api) (require 'codegeex-overlay) - (defvar codegeex-json-string-cache nil "Mainly for debugging but maybe will be useful")