branch: externals/ellama commit 1c52902d4de4933b61cd3a3bcdd3f74cadd31f9f Merge: 1b71217434 22dcffff59 Author: Sergey Kostyaev <s-kosty...@users.noreply.github.com> Commit: GitHub <nore...@github.com>
Merge pull request #235 from s-kostyaev/add-context-header-line Add ellama context header and mode line modes --- NEWS.org | 8 ++ README.org | 215 ++++++++++++++++++++++++++++---------------- docs/changelog.org | 3 + ellama.el | 245 +++++++++++++++++++++++++++++++++------------------ tests/test-ellama.el | 61 +++++-------- 5 files changed, 334 insertions(+), 198 deletions(-) diff --git a/NEWS.org b/NEWS.org index e7b76e2678..9ec0c38ba8 100644 --- a/NEWS.org +++ b/NEWS.org @@ -1,3 +1,11 @@ +* Version 1.3.0 +- Implemented ellama context header line and mode line features. +- Added ~ellama-context-header-line-mode~, ~ellama-context-mode-line-mode~ and + its global versions. +- Session renaming functionality improvements. +- Improved session deletion. +- Renamed ~ellama-session-remove~ to ~ellama-session-delete~. +- Removed ~ellama-long-lines-length~ customization and related usage * Version 1.2.5 - Fix scroll function. * Version 1.2.4 diff --git a/README.org b/README.org index aa2f5a1580..1bda4fb572 100644 --- a/README.org +++ b/README.org @@ -32,72 +32,74 @@ Without any configuration, the first available ollama model will be used. You can customize ellama configuration like this: #+BEGIN_SRC emacs-lisp -(use-package ellama - :ensure t - :bind ("C-c e" . ellama-transient-main-menu) - :init - ;; setup key bindings - ;; (setopt ellama-keymap-prefix "C-c e") - ;; language you want ellama to translate to - (setopt ellama-language "German") - ;; could be llm-openai for example - (require 'llm-ollama) - (setopt ellama-provider - (make-llm-ollama - ;; this model should be pulled to use it - ;; value should be the same as you print in terminal during pull - :chat-model "llama3:8b-instruct-q8_0" - :embedding-model "nomic-embed-text" - :default-chat-non-standard-params '(("num_ctx" . 8192)))) - (setopt ellama-summarization-provider - (make-llm-ollama - :chat-model "qwen2.5:3b" - :embedding-model "nomic-embed-text" - :default-chat-non-standard-params '(("num_ctx" . 32768)))) - (setopt ellama-coding-provider - (make-llm-ollama - :chat-model "qwen2.5-coder:3b" - :embedding-model "nomic-embed-text" - :default-chat-non-standard-params '(("num_ctx" . 32768)))) - ;; Predefined llm providers for interactive switching. - ;; You shouldn't add ollama providers here - it can be selected interactively - ;; without it. It is just example. - (setopt ellama-providers - '(("zephyr" . (make-llm-ollama - :chat-model "zephyr:7b-beta-q6_K" - :embedding-model "zephyr:7b-beta-q6_K")) - ("mistral" . (make-llm-ollama - :chat-model "mistral:7b-instruct-v0.2-q6_K" - :embedding-model "mistral:7b-instruct-v0.2-q6_K")) - ("mixtral" . (make-llm-ollama - :chat-model "mixtral:8x7b-instruct-v0.1-q3_K_M-4k" - :embedding-model "mixtral:8x7b-instruct-v0.1-q3_K_M-4k")))) - ;; Naming new sessions with llm - (setopt ellama-naming-provider - (make-llm-ollama - :chat-model "llama3:8b-instruct-q8_0" - :embedding-model "nomic-embed-text" - :default-chat-non-standard-params '(("stop" . ("\n"))))) - (setopt ellama-naming-scheme 'ellama-generate-name-by-llm) - ;; Translation llm provider - (setopt ellama-translation-provider - (make-llm-ollama - :chat-model "qwen2.5:3b" - :embedding-model "nomic-embed-text" - :default-chat-non-standard-params - '(("num_ctx" . 32768)))) - (setopt ellama-extraction-provider (make-llm-ollama - :chat-model "qwen2.5-coder:7b-instruct-q8_0" - :embedding-model "nomic-embed-text" - :default-chat-non-standard-params - '(("num_ctx" . 32768)))) - ;; customize display buffer behaviour - ;; see ~(info "(elisp) Buffer Display Action Functions")~ - (setopt ellama-chat-display-action-function #'display-buffer-full-frame) - (setopt ellama-instant-display-action-function #'display-buffer-at-bottom) - :config - ;; send last message in chat buffer with C-c C-c - (add-hook 'org-ctrl-c-ctrl-c-hook #'ellama-chat-send-last-message)) + (use-package ellama + :ensure t + :bind ("C-c e" . ellama-transient-main-menu) + :init + ;; setup key bindings + ;; (setopt ellama-keymap-prefix "C-c e") + ;; language you want ellama to translate to + (setopt ellama-language "German") + ;; could be llm-openai for example + (require 'llm-ollama) + (setopt ellama-provider + (make-llm-ollama + ;; this model should be pulled to use it + ;; value should be the same as you print in terminal during pull + :chat-model "llama3:8b-instruct-q8_0" + :embedding-model "nomic-embed-text" + :default-chat-non-standard-params '(("num_ctx" . 8192)))) + (setopt ellama-summarization-provider + (make-llm-ollama + :chat-model "qwen2.5:3b" + :embedding-model "nomic-embed-text" + :default-chat-non-standard-params '(("num_ctx" . 32768)))) + (setopt ellama-coding-provider + (make-llm-ollama + :chat-model "qwen2.5-coder:3b" + :embedding-model "nomic-embed-text" + :default-chat-non-standard-params '(("num_ctx" . 32768)))) + ;; Predefined llm providers for interactive switching. + ;; You shouldn't add ollama providers here - it can be selected interactively + ;; without it. It is just example. + (setopt ellama-providers + '(("zephyr" . (make-llm-ollama + :chat-model "zephyr:7b-beta-q6_K" + :embedding-model "zephyr:7b-beta-q6_K")) + ("mistral" . (make-llm-ollama + :chat-model "mistral:7b-instruct-v0.2-q6_K" + :embedding-model "mistral:7b-instruct-v0.2-q6_K")) + ("mixtral" . (make-llm-ollama + :chat-model "mixtral:8x7b-instruct-v0.1-q3_K_M-4k" + :embedding-model "mixtral:8x7b-instruct-v0.1-q3_K_M-4k")))) + ;; Naming new sessions with llm + (setopt ellama-naming-provider + (make-llm-ollama + :chat-model "llama3:8b-instruct-q8_0" + :embedding-model "nomic-embed-text" + :default-chat-non-standard-params '(("stop" . ("\n"))))) + (setopt ellama-naming-scheme 'ellama-generate-name-by-llm) + ;; Translation llm provider + (setopt ellama-translation-provider + (make-llm-ollama + :chat-model "qwen2.5:3b" + :embedding-model "nomic-embed-text" + :default-chat-non-standard-params + '(("num_ctx" . 32768)))) + (setopt ellama-extraction-provider (make-llm-ollama + :chat-model "qwen2.5-coder:7b-instruct-q8_0" + :embedding-model "nomic-embed-text" + :default-chat-non-standard-params + '(("num_ctx" . 32768)))) + ;; customize display buffer behaviour + ;; see ~(info "(elisp) Buffer Display Action Functions")~ + (setopt ellama-chat-display-action-function #'display-buffer-full-frame) + (setopt ellama-instant-display-action-function #'display-buffer-at-bottom) + :config + ;; show ellama context in header line in all buffers + (ellama-context-header-line-global-mode +1) + ;; send last message in chat buffer with C-c C-c + (add-hook 'org-ctrl-c-ctrl-c-hook #'ellama-chat-send-last-message)) #+END_SRC ** Commands @@ -240,9 +242,9 @@ as a specified format using Ellama. Load ellama session from file. -*** ellama-session-remove +*** ellama-session-delete -Remove ellama session. +Delete ellama session. *** ellama-session-switch @@ -340,7 +342,7 @@ Ellama, using the ~ellama-keymap-prefix~ prefix (not set by default): | "s c" | ellama-summarize-killring | Summarize killring | | "s l" | ellama-load-session | Session Load | | "s r" | ellama-session-rename | Session rename | -| "s d" | ellama-session-remove | Session delete | +| "s d" | ellama-session-delete | Delete delete | | "s a" | ellama-session-switch | Session activate | | "P" | ellama-proofread | Proofread | | "i w" | ellama-improve-wording | Improve wording | @@ -383,6 +385,7 @@ There are many supported providers: ~ollama~, ~open ai~, ~vertex~, ~GPT4All~. For more information see [[https://elpa.gnu.org/packages/llm.html][llm documentation]]. - ~ellama-providers~: association list of model llm providers with name as key. +- ~ellama-spinner-enabled~: Enable spinner during text generation. - ~ellama-spinner-type~: Spinner type for ellama. Default type is ~progress-bar~. - ~ellama-ollama-binary~: Path to ollama binary. @@ -401,9 +404,6 @@ argument generated text string. - ~ellama-sessions-directory~: Directory for saved ellama sessions. - ~ellama-major-mode~: Major mode for ellama commands. Org mode by default. -- ~ellama-long-lines-length~: Long lines length for fill paragraph - call. Too low value can break generated code by splitting long - comment lines. Default value 100. - ~ellama-session-auto-save~: Automatically save ellama sessions if set. Enabled by default. - ~ellama-naming-scheme~: How to name new sessions. @@ -427,7 +427,6 @@ argument generated text string. - ~ellama-context-poshandler~: Position handler for displaying context buffer. ~posframe-poshandler-frame-top-center~ will be used if not set. - ~ellama-context-border-width~: Border width for the context buffer. -- ~ellama-context-element-padding-size~: Padding size for context elements. - ~ellama-session-remove-reasoning~: Remove internal reasoning from the session after ellama provide an answer. This can improve long-term communication with reasoning models. Enabled by default. @@ -439,13 +438,81 @@ argument generated text string. ellama output to enhance the versatility of reasoning models across diverse applications. - ~ellama-context-posframe-enabled~: Enable showing posframe with - ellama context. Enabled by default. + ellama context. - ~ellama-manage-context-display-action-function~: Display action function for ~ellama-render-context~. Default value ~display-buffer-same-window~. - ~ellama-preview-context-element-display-action-function~: Display action function for ~ellama-preview-context-element~. +** Minor modes + +*** ellama-context-header-line-mode + +*Description:* +Toggle the Ellama Context header line mode. This minor mode updates the header line to display +context-specific information. + +*Usage:* +To enable or disable ~ellama-context-header-line-mode~, use the command: + + M-x ellama-context-header-line-mode + +When enabled, this mode adds a hook to ~window-state-change-hook~ to update the header line whenever +the window state changes. It also calls ~ellama-context-update-header-line~ to initialize the header +line with context-specific information. + +When disabled, it removes the evaluation of ~(:eval (ellama-context-line))~ from +~header-line-format~. + +*** ellama-context-header-line-global-mode + +*Description:* +Globalized version of ~ellama-context-header-line-mode~. This mode ensures that +~ellama-context-header-line-mode~ is enabled in all buffers. + +*Usage:* +To enable or disable ~ellama-context-header-line-global-mode~, use the command: + + M-x ellama-context-header-line-global-mode + +This globalized minor mode provides a convenient way to ensure that context-specific header line +information is always available, regardless of the buffer being edited. + +*** ellama-context-mode-line-mode + +*Description:* +Toggle the Ellama Context mode line mode. This minor mode updates the mode line +to display context-specific information. + +*Usage:* +To enable or disable ~ellama-context-mode-line-mode~, use the command: + + M-x ellama-context-mode-line-mode + +When enabled, this mode adds a hook to ~window-state-change-hook~ to update the +mode line whenever the window state changes. It also calls +~ellama-context-update-mode-line~ to initialize the mode line with +context-specific information. + +When disabled, it removes the evaluation of ~(:eval (ellama-context-line))~ from +~mode-line-format~. + +*** ellama-context-mode-line-global-mode + +*Description:* +Globalized version of ~ellama-context-mode-line-mode~. This mode ensures that +~ellama-context-mode-line-mode~ is enabled in all buffers. + +*Usage:* +To enable or disable ~ellama-context-mode-line-global-mode~, use the command: + + M-x ellama-context-mode-line-global-mode + +This globalized minor mode provides a convenient way to ensure that +context-specific mode line information is always available, regardless of the +buffer being edited. + ** Acknowledgments Thanks [[https://github.com/jmorganca][Jeffrey Morgan]] for excellent project [[https://github.com/jmorganca/ollama][ollama]]. This project diff --git a/docs/changelog.org b/docs/changelog.org new file mode 100644 index 0000000000..a713a2d381 --- /dev/null +++ b/docs/changelog.org @@ -0,0 +1,3 @@ +Based on this git log write short changelog in markdown list format. Do not add +any headings or anknowledgements. Every changelog element should be ended with +full stop. diff --git a/ellama.el b/ellama.el index 20c92023ed..e6171a1922 100644 --- a/ellama.el +++ b/ellama.el @@ -5,8 +5,8 @@ ;; Author: Sergey Kostyaev <sskosty...@gmail.com> ;; URL: http://github.com/s-kostyaev/ellama ;; Keywords: help local tools -;; Package-Requires: ((emacs "28.1") (llm "0.22.0") (spinner "1.7.4") (transient "0.7") (compat "29.1") (posframe "1.4.0")) -;; Version: 1.2.5 +;; Package-Requires: ((emacs "28.1") (llm "0.22.0") (transient "0.7") (compat "29.1")) +;; Version: 1.3.0 ;; SPDX-License-Identifier: GPL-3.0-or-later ;; Created: 8th Oct 2023 @@ -38,10 +38,8 @@ (require 'eieio) (require 'llm) (require 'llm-provider-utils) -(require 'spinner) (require 'transient) (require 'compat) -(require 'posframe) (eval-when-compile (require 'rx)) (defgroup ellama nil @@ -116,13 +114,22 @@ Make reasoning models more useful for many cases." :type '(alist :key-type string :value-type (sexp :validate llm-standard-provider-p))) +(defvar spinner-types) + (defcustom ellama-spinner-type 'progress-bar "Spinner type for ellama." :group 'ellama - :type `(choice ,@(mapcar - (lambda (type) - `(const ,(car type))) - spinner-types))) + :type `(choice ,@(if (boundp 'spinner-types) + (mapcar + (lambda (type) + `(const ,(car type))) + spinner-types) + '(const progress-bar)))) + +(defcustom ellama-spinner-enabled nil + "Enable spinner during text generation." + :group 'ellama + :type 'boolean) (defcustom ellama-command-map (let ((map (make-sparse-keymap))) @@ -140,7 +147,7 @@ Make reasoning models more useful for many cases." ;; session (define-key map (kbd "s l") 'ellama-load-session) (define-key map (kbd "s r") 'ellama-session-rename) - (define-key map (kbd "s d") 'ellama-session-remove) + (define-key map (kbd "s d") 'ellama-session-delete) (define-key map (kbd "s a") 'ellama-session-switch) ;; improve (define-key map (kbd "i w") 'ellama-improve-wording) @@ -449,12 +456,6 @@ It should be a function with single argument generated text string." :group 'ellama :type 'symbol) -(defcustom ellama-long-lines-length 100 - "Long lines length for fill paragraph call. -Too low value can break generated code by splitting long comment lines." - :group 'ellama - :type 'integer) - (defcustom ellama-translate-italic t "Translate italic during markdown to org transformations." :group 'ellama @@ -494,6 +495,7 @@ Too low value can break generated code by splitting long comment lines." (define-minor-mode ellama-request-mode "Minor mode for ellama buffers with active request to llm." :interactive nil + :lighter " ellama:generating" :keymap '(([remap keyboard-quit] . ellama--cancel-current-request-and-quit)) (if ellama-request-mode (add-hook 'kill-buffer-hook 'ellama--cancel-current-request nil t) @@ -522,8 +524,7 @@ Too low value can break generated code by splitting long comment lines." (if ellama-fill-paragraphs (with-temp-buffer (insert (propertize text 'hard t)) - (let ((fill-column ellama-long-lines-length) - (use-hard-newlines t)) + (let ((use-hard-newlines t)) (fill-region (point-min) (point-max) nil t t)) (buffer-substring-no-properties (point-min) (point-max))) text)) @@ -587,8 +588,7 @@ Too low value can break generated code by splitting long comment lines." ;; filling long lines (goto-char beg) (when ellama-fill-paragraphs - (let ((fill-column ellama-long-lines-length) - (use-hard-newlines t)) + (let ((use-hard-newlines t)) (fill-region beg end nil t t)))) (defun ellama--replace-outside-of-code-blocks (text) @@ -823,9 +823,12 @@ If EPHEMERAL non nil new session will not be associated with any file." (defun ellama--cancel-current-request () "Cancel current running request." + (declare-function spinner-stop "ext:spinner") (when ellama--current-request (llm-cancel-request ellama--current-request) - (spinner-stop) + (when ellama-spinner-enabled + (require 'spinner) + (spinner-stop)) (setq ellama--current-request nil))) (defun ellama--cancel-current-request-and-quit () @@ -953,27 +956,29 @@ If EPHEMERAL non nil new session will not be associated with any file." `((ignore . (,ellama-chat-display-action-function))))))) ;;;###autoload -(defun ellama-session-remove () - "Remove ellama session." +(defun ellama-session-delete () + "Delete ellama session." (interactive) (let* ((id (completing-read "Select session to remove: " (hash-table-keys ellama--active-sessions))) (buffer (ellama-get-session-buffer id)) (file (buffer-file-name buffer)) - (session-file (ellama--get-session-file-name file)) - (translation-file (ellama--get-translation-file-name file))) + (session-file (when file (ellama--get-session-file-name file))) + (translation-file (when file (ellama--get-translation-file-name file)))) (kill-buffer buffer) - (delete-file file t) - (delete-file session-file t) + (when file (delete-file file t)) + (when session-file (delete-file session-file t)) (mapc (lambda (buf) - (when (and (buffer-file-name buf) - (file-equal-p (buffer-file-name buf) - translation-file)) + (when (and + translation-file + (buffer-file-name buf) + (file-equal-p (buffer-file-name buf) + translation-file)) (kill-buffer buf))) (buffer-list)) - (when (file-exists-p translation-file) + (when (and translation-file (file-exists-p translation-file)) (delete-file translation-file t)))) (defun ellama-activate-session (id) @@ -996,39 +1001,44 @@ If EPHEMERAL non nil new session will not be associated with any file." (defun ellama-session-rename () "Rename current ellama session." (interactive) - (when-let* ((id (if ellama--current-session - (ellama-session-id ellama--current-session) - ellama--current-session-id)) - (buffer (ellama-get-session-buffer id)) - (session (with-current-buffer buffer - ellama--current-session)) - (file-name (buffer-file-name buffer)) - (file-ext (file-name-extension file-name)) - (dir (file-name-directory file-name)) - (session-file-name (ellama--get-session-file-name file-name)) - (new-id (read-string - "New session name: " - id)) - (new-file-name (file-name-concat - dir - (concat new-id "." file-ext))) - (new-session-file-name - (ellama--get-session-file-name new-file-name))) - (with-current-buffer buffer - (set-visited-file-name new-file-name)) - (when (file-exists-p file-name) + (let* ((id (if ellama--current-session + (ellama-session-id ellama--current-session) + ellama--current-session-id)) + (buffer (when id (ellama-get-session-buffer id))) + (session (when buffer (with-current-buffer buffer + ellama--current-session))) + (file-name (when buffer (buffer-file-name buffer))) + (file-ext (when file-name (file-name-extension file-name))) + (dir (when file-name (file-name-directory file-name))) + (session-file-name (when file-name (ellama--get-session-file-name file-name))) + (new-id (read-string + "New session name: " + id)) + (new-file-name (when dir (file-name-concat + dir + (concat new-id "." file-ext)))) + (new-session-file-name + (when new-file-name (ellama--get-session-file-name new-file-name)))) + (when new-file-name (with-current-buffer buffer + (set-visited-file-name new-file-name))) + (when buffer (with-current-buffer buffer + (rename-buffer (or new-file-name new-id)))) + (when (and file-name (file-exists-p file-name)) (rename-file file-name new-file-name)) - (when (file-exists-p session-file-name) + (when (and session-file-name (file-exists-p session-file-name)) (rename-file session-file-name new-session-file-name)) - (setf (ellama-session-id session) new-id) + (when session (setf (ellama-session-id session) new-id)) (when (equal ellama--current-session-id id) (setq ellama--current-session-id new-id)) (remhash id ellama--active-sessions) - (puthash new-id buffer ellama--active-sessions))) + (puthash new-id buffer ellama--active-sessions) + (when (and buffer ellama-session-auto-save) + (with-current-buffer buffer + (save-buffer))))) (defvar ellama--context-buffer " *ellama-context*") -(defcustom ellama-context-posframe-enabled t +(defcustom ellama-context-posframe-enabled nil "Enable showing posframe with ellama context." :group 'ellama :type 'boolean) @@ -1040,8 +1050,7 @@ If EPHEMERAL non nil new session will not be associated with any file." (setq ellama--global-context nil) (with-current-buffer ellama--context-buffer (erase-buffer)) - (when ellama-context-posframe-enabled - (posframe-hide ellama--context-buffer))) + (ellama-update-context-show)) ;; Context elements @@ -1070,32 +1079,95 @@ If EPHEMERAL non nil new session will not be associated with any file." :group 'ellama :type 'integer) -(defcustom ellama-context-element-padding-size 20 - "Padding size for context elements." - :group 'ellama - :type 'integer) - -(defun ellama-update-context-posframe-show () - "Update and show context posframe." +(defun ellama-update-context-show () + "Update and show context in posframe of header line." + (declare-function posframe-show "ext:posframe") + (declare-function posframe-hide "ext:posframe") + (with-current-buffer ellama--context-buffer + (erase-buffer) + (when ellama--global-context + (insert (format + " ellama ctx: %s" + (string-join + (mapcar + (lambda (el) + (ellama-context-element-display el)) + ellama--global-context) + " "))))) (when ellama-context-posframe-enabled - (with-current-buffer ellama--context-buffer - (erase-buffer) - (when ellama--global-context - (insert (format - "context: %s" - (string-join - (mapcar - (lambda (el) - (string-pad - (ellama-context-element-display el) ellama-context-element-padding-size)) - ellama--global-context) - " "))))) + (require 'posframe) (if ellama--global-context (posframe-show ellama--context-buffer :poshandler ellama-context-poshandler :internal-border-width ellama-context-border-width) - (posframe-hide ellama--context-buffer)))) + (posframe-hide ellama--context-buffer))) + (ellama-context-update-header-line)) + +(defface ellama-face '((t (:inherit shadow))) + "Base face for all ellama things.") + +(defface ellama-context-line-face '((t (:inherit (mode-line-buffer-id ellama-face)))) + "Face for ellama context line.") + +(defun ellama-context-line () + "Return current global context line." + (propertize (with-current-buffer ellama--context-buffer + (buffer-substring-no-properties + (point-min) (point-max))) + 'help-echo "mouse-1: manage ellama context" + 'mouse-face 'header-line-format + 'face 'ellama-context-line-face + 'keymap (let ((m (make-sparse-keymap))) + (define-key m [header-line mouse-1] #'ellama-transient-context-menu) + (define-key m [mode-line mouse-1] #'ellama-transient-context-menu) + m))) + +;;;###autoload +(define-minor-mode ellama-context-header-line-mode + "Toggle Ellama Context header line mode." + :group 'ellama + (add-hook 'window-state-change-hook #'ellama-context-update-header-line) + (if ellama-context-header-line-mode + (ellama-context-update-header-line) + (setq header-line-format (delete '(:eval (ellama-context-line)) header-line-format)))) + +;;;###autoload +(define-globalized-minor-mode ellama-context-header-line-global-mode + ellama-context-header-line-mode + ellama-context-header-line-mode) + +(defun ellama-context-update-header-line () + "Update and display context information in the header line." + (if (and ellama-context-header-line-mode ellama--global-context) + (add-to-list 'header-line-format '(:eval (ellama-context-line)) t) + (setq header-line-format (delete '(:eval (ellama-context-line)) header-line-format)))) + +;;;###autoload +(define-minor-mode ellama-context-mode-line-mode + "Toggle Ellama Context mode line mode." + :group 'ellama + (add-hook 'window-state-change-hook #'ellama-context-update-mode-line) + (if ellama-context-mode-line-mode + (ellama-context-update-mode-line) + (setq mode-line-format (delete '(:eval (ellama-context-line)) mode-line-format)))) + +;;;###autoload +(define-globalized-minor-mode ellama-context-mode-line-global-mode + ellama-context-mode-line-mode + ellama-context-mode-line-mode) + +(defun ellama-context-turn-on-mode-line-mode () + "Turn on `ellama-context-mode-line-mode' if appropriate." + (when (or (eq major-mode 'text-mode) + (derived-mode-p 'text-mode)) + (ellama-context-mode-line-mode 1))) + +(defun ellama-context-update-mode-line () + "Update and display context information in the mode line." + (if (and ellama-context-mode-line-mode ellama--global-context) + (add-to-list 'mode-line-format '(:eval (ellama-context-line)) t) + (setq mode-line-format (delete '(:eval (ellama-context-line)) mode-line-format)))) (cl-defmethod ellama-context-element-add ((element ellama-context-element)) "Add the ELEMENT to the Ellama context." @@ -1104,7 +1176,7 @@ If EPHEMERAL non nil new session will not be associated with any file." :test #'equal-including-properties) (setf ellama--global-context (nreverse ellama--global-context)) (get-buffer-create ellama--context-buffer t) - (ellama-update-context-posframe-show)) + (ellama-update-context-show)) (defcustom ellama-manage-context-display-action-function #'display-buffer-same-window "Display action function for `ellama-render-context'." @@ -1203,7 +1275,7 @@ If EPHEMERAL non nil new session will not be associated with any file." (when-let ((elt (get-text-property (point) 'context-element))) (ellama-remove-context-element elt) (ellama-manage-context) - (ellama-update-context-posframe-show))) + (ellama-update-context-show))) ;; Buffer context element @@ -1450,7 +1522,6 @@ If EPHEMERAL non nil new session will not be associated with any file." (with-temp-buffer (insert (propertize content 'hard t)) (let ((fill-prefix "> ") - (fill-column ellama-long-lines-length) (use-hard-newlines t) (comment-start ">") (comment-empty-lines t)) @@ -1803,6 +1874,8 @@ failure (with BUFFER current). :on-done ON-DONE -- ON-DONE a function or list of functions that's called with the full response text when the request completes (with BUFFER current)." + (declare-function spinner-start "ext:spinner") + (declare-function spinner-stop "ext:spinner") (let* ((session-id (plist-get args :session-id)) (session (or (plist-get args :session) (when session-id @@ -1869,7 +1942,9 @@ failure (with BUFFER current). (setq ellama--change-group (prepare-change-group)) (activate-change-group ellama--change-group) (ellama-set-markers start end point) - (spinner-start ellama-spinner-type) + (when ellama-spinner-enabled + (require 'spinner) + (spinner-start ellama-spinner-type)) (let ((request (llm-chat-streaming provider llm-prompt @@ -1883,7 +1958,8 @@ failure (with BUFFER current). text))) (with-current-buffer buffer (accept-change-group ellama--change-group) - (spinner-stop) + (when ellama-spinner-enabled + (spinner-stop)) (if (and (listp donecb) (functionp (car donecb))) (mapc (lambda (fn) (funcall fn text)) @@ -1907,7 +1983,8 @@ failure (with BUFFER current). (lambda (_ msg) (with-current-buffer buffer (cancel-change-group ellama--change-group) - (spinner-stop) + (when ellama-spinner-enabled + (spinner-stop)) (funcall errcb msg) (setq ellama--current-request nil) (ellama-request-mode -1)))))) @@ -2944,7 +3021,7 @@ Call CALLBACK on result list of strings. ARGS contains keys for fine control. [["Session Commands" ("l" "Load Session" ellama-load-session) ("r" "Rename Session" ellama-session-rename) - ("d" "Remove Session" ellama-session-remove) + ("d" "Delete Session" ellama-session-delete) ("a" "Activate Session" ellama-session-switch)] ["Quit" ("q" "Quit" transient-quit-one)]]) diff --git a/tests/test-ellama.el b/tests/test-ellama.el index b88e2226ce..6b47022191 100644 --- a/tests/test-ellama.el +++ b/tests/test-ellama.el @@ -251,70 +251,70 @@ ```tex \\documentclass{article} \\usepackage{tikz} \\begin{document} -\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) {Text}; +\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text}; \\end{tikzpicture} \\end{document} ``` This code will create a rectangle with a blue border and light -blue filling. You can replace \'Text\' with your desired text or other TikZ -elements."))) +blue filling. You can replace \'Text\' with your desired text +or other TikZ elements."))) (should (string-equal result "Here is your TikZ code for a blue rectangle: #+BEGIN_SRC tex \\documentclass{article} \\usepackage{tikz} \\begin{document} -\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) {Text}; +\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text}; \\end{tikzpicture} \\end{document} #+END_SRC This code will create a rectangle with a blue border and light -blue filling. You can replace \'Text\' with your desired text or other TikZ -elements.")))) +blue filling. You can replace \'Text\' with your desired text +or other TikZ elements.")))) (ert-deftest test-ellama-md-to-org-code-hard () (let ((result (ellama--translate-markdown-to-org-filter "Here is your TikZ code for a blue rectangle: ``` \\documentclass{article} \\usepackage{tikz} \\begin{document} -\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) {Text}; +\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text}; \\end{tikzpicture} \\end{document} ``` This code will create a rectangle with a blue border and light -blue filling. You can replace \'Text\' with your desired text or other TikZ -elements."))) +blue filling. You can replace \'Text\' with your desired text or other +TikZ elements."))) (should (string-equal result "Here is your TikZ code for a blue rectangle: #+BEGIN_SRC \\documentclass{article} \\usepackage{tikz} \\begin{document} -\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) {Text}; +\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text}; \\end{tikzpicture} \\end{document} #+END_SRC This code will create a rectangle with a blue border and light -blue filling. You can replace \'Text\' with your desired text or other TikZ -elements.")))) +blue filling. You can replace \'Text\' with your desired text or other +TikZ elements.")))) (ert-deftest test-ellama-md-to-org-code-nightmare () (let ((result (ellama--translate-markdown-to-org-filter "Here is your TikZ code for a blue rectangle: ``` \\documentclass{article} \\usepackage{tikz} \\begin{document} -\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) {Text}; +\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text}; \\end{tikzpicture} \\end{document}```This code will create a rectangle with a blue border and light -blue filling. You can replace \'Text\' with your desired text or other TikZ -elements."))) +blue filling. You can replace \'Text\' with your desired text +or other TikZ elements."))) (should (string-equal result "Here is your TikZ code for a blue rectangle: #+BEGIN_SRC \\documentclass{article} \\usepackage{tikz} \\begin{document} -\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) {Text}; +\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text}; \\end{tikzpicture} \\end{document} #+END_SRC This code will create a rectangle with a blue border and light -blue filling. You can replace \'Text\' with your desired text or other TikZ -elements.")))) +blue filling. You can replace \'Text\' with your desired text +or other TikZ elements.")))) (ert-deftest test-ellama-md-to-org-code-multiple-bad-blocks () (let ((result (ellama--translate-markdown-to-org-filter "Some text: @@ -380,7 +380,8 @@ $P_\\theta$ (should (string-equal result "This")))) (ert-deftest test-ellama-md-to-org-code-snake-case () - (let ((result (ellama--translate-markdown-to-org-filter "```python + (let* ((fill-column 70) + (result (ellama--translate-markdown-to-org-filter "```python # Example of snake case variables and functions # Variable names using snake_case @@ -407,27 +408,7 @@ In this example: - The function name `calculate_average_score` also follows the snake_case convention. Snake case helps improve readability, especially in languages that are sensitive to capitalization like Python."))) - (should (string-equal result "#+BEGIN_SRC python -# Example of snake case variables and functions - -# Variable names using snake_case -student_name = \"Alice Johnson\" -class_name = \"Mathematics\" -grade_level = 10 - -# Function name using snake_case -def calculate_average_score(math_score, science_score, english_score): - average_score = (math_score + science_score + english_score) / 3 - return average_score - -# Using the function -student_math_score = 85 -student_science_score = 90 -student_english_score = 78 - -average_score = calculate_average_score(student_math_score, student_science_score, student_english_score) -print(f\"The average score of {student_name} in {class_name} is: {average_score:.2f}\") -#+END_SRC\n\nIn this example:\n- Variable names like ~student/name~, ~class/name~, and ~grade/level~ use snake/case.\n- The function name ~calculate/average/score~ also follows the snake_case convention.\n\nSnake case helps improve readability, especially in languages that are sensitive to capitalization\nlike Python.")))) + (should (string-equal result "#+BEGIN_SRC python\n# Example of snake case variables and functions\n\n# Variable names using snake_case\nstudent_name = \"Alice Johnson\"\nclass_name = \"Mathematics\"\ngrade_level = 10\n\n# Function name using snake_case\ndef calculate_average_score(math_score, science_score, english_score):\n average_score = (math_score + science_score + english_score) / 3\n return average_score\n\n# Using the function\nstudent_math_score = 85\nstudent_science_s [...] (ert-deftest test-ellama--fix-file-name () (should (string=