branch: externals/ellama commit 75f158490927e9def13655fe09f23dfad62008e3 Author: Sergey Kostyaev <sskosty...@gmail.com> Commit: Sergey Kostyaev <sskosty...@gmail.com>
Implement common prefix and string manipulation functions Implemented `ellama-max-common-prefix` to find the maximum common prefix of two strings. Added `ellama--string-without-last-line` to remove the last line from a given string. Updated `ellama--insert` function to use these new utilities for better text handling during streaming. --- ellama.el | 52 +++++++++++++++++++++++++++++++++++++++++----------- tests/test-ellama.el | 25 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/ellama.el b/ellama.el index 68dec2ec72..603180f78a 100644 --- a/ellama.el +++ b/ellama.el @@ -1188,6 +1188,22 @@ EVENT is an argument for mweel scroll." "Enable auto scroll." (setq ellama--stop-scroll nil)) +(defun ellama-max-common-prefix (s1 s2) + "Return the maximum common prefix of strings S1 and S2." + (let ((i 0) + (min-length (min (length s1) (length s2)))) + (while (and (< i min-length) + (eq (aref s1 i) (aref s2 i))) + (setq i (1+ i))) + (substring s1 0 i))) + +(defun ellama--string-without-last-line (s) + "Remove last line from string S." + (string-join + (reverse (cdr (reverse (string-lines + s)))) + "\n")) + (defun ellama--insert (buffer point filter) "Insert text during streaming. @@ -1197,7 +1213,8 @@ FILTER is a function for text transformation." (with-current-buffer buffer (let* ((end-marker (make-marker)) - (previous-filtered-text-length 0)) + (previous-filtered-text "") + (safe-common-prefix "")) (set-marker end-marker (or point (point))) (set-marker-insertion-type end-marker t) (lambda @@ -1207,21 +1224,34 @@ FILTER is a function for text transformation." (goto-char end-marker) (let* ((filtered-text (funcall filter text)) - (delta (substring filtered-text - previous-filtered-text-length - (length filtered-text)))) + (common-prefix (concat + safe-common-prefix + (ellama-max-common-prefix + (string-remove-prefix + safe-common-prefix + filtered-text) + (string-remove-prefix + safe-common-prefix + previous-filtered-text)))) + (wrong-chars-cnt (- (length previous-filtered-text) + (length common-prefix))) + (delta (string-remove-prefix common-prefix filtered-text))) + (delete-char (- wrong-chars-cnt)) (insert delta) - (when (and ellama-fill-paragraphs - (pcase ellama-fill-paragraphs - ((cl-type function) (funcall ellama-fill-paragraphs)) - ((cl-type boolean) ellama-fill-paragraphs) - ((cl-type list) (and (apply #'derived-mode-p - ellama-fill-paragraphs))))) + (when (and + (not (eq major-mode 'org-mode)) + ellama-fill-paragraphs + (pcase ellama-fill-paragraphs + ((cl-type function) (funcall ellama-fill-paragraphs)) + ((cl-type boolean) ellama-fill-paragraphs) + ((cl-type list) (and (apply #'derived-mode-p + ellama-fill-paragraphs))))) (fill-paragraph)) (set-marker end-marker (point)) (when (and ellama-auto-scroll (not ellama--stop-scroll)) (ellama--scroll buffer end-marker)) - (setq previous-filtered-text-length (length filtered-text))))))))) + (setq safe-common-prefix (ellama--string-without-last-line common-prefix)) + (setq previous-filtered-text filtered-text)))))))) (defun ellama--handle-partial (insert-text insert-reasoning reasoning-buffer) "Handle partial llm callback. diff --git a/tests/test-ellama.el b/tests/test-ellama.el index 394184c861..ddb1a04555 100644 --- a/tests/test-ellama.el +++ b/tests/test-ellama.el @@ -437,6 +437,31 @@ _more italic_"))) $P_\\theta$ /more italic/")))) +(defun ellama-test-max-common-prefix () + "Test the `ellama-max-common-prefix` function." + (should (equal (ellama-max-common-prefix "" "") "")) + (should (equal (ellama-max-common-prefix "abc" "abcd") "abc")) + (should (equal (ellama-max-common-prefix "abcd" "abc") "abc")) + (should (equal (ellama-max-common-prefix "abcdef" "abcefg") "abc")) + (should (equal (ellama-max-common-prefix "a" "b") "")) + (should (equal (ellama-max-common-prefix "a" "") "")) + (should (equal (ellama-max-common-prefix "" "b") ""))) + +(ert-deftest ellama-test-max-common-prefix () + "Run the tests for `ellama-max-common-prefix`." + (ellama-test-max-common-prefix)) + +(ert-deftest ellama--string-without-last-line-test () + "Test `ellama--string-without-last-line` function." + (should (equal (ellama--string-without-last-line "Line1\nLine2\nLine3") + "Line1\nLine2")) + (should (equal (ellama--string-without-last-line "SingleLine") + "")) + (should (equal (ellama--string-without-last-line "") + "")) + (should (equal (ellama--string-without-last-line "Line1\nLine2") + "Line1"))) + (provide 'test-ellama) ;;; test-ellama.el ends here