emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/eat 0638ff6212: Experimentally support for multi-column ch


From: ELPA Syncer
Subject: [nongnu] elpa/eat 0638ff6212: Experimentally support for multi-column characters
Date: Sun, 27 Nov 2022 16:58:35 -0500 (EST)

branch: elpa/eat
commit 0638ff621267c22877f953842f5abf1aefc66e88
Author: Akib Azmain Turja <akib@disroot.org>
Commit: Akib Azmain Turja <akib@disroot.org>

    Experimentally support for multi-column characters
    
    * eat.el (eat--t-move-before-to-safe, eat--t-make-pos-safe)
    (eat--t-fix-partial-multi-col-char): New function.
    * eat.el (eat--t-write): Handle multi-column characters.
    * eat.el (eat--t-insert-char, eat--t-delete-char)
    (eat--t-erase-char): Handle multi-column characters on the
    display while manipulating text.
---
 eat.el | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 165 insertions(+), 10 deletions(-)

diff --git a/eat.el b/eat.el
index a7b857667f..f7cc934988 100644
--- a/eat.el
+++ b/eat.el
@@ -2299,6 +2299,68 @@ of range, place cursor at the edge of display."
 CHARSET should be one of `g0', `g1', `g2' and `g3'."
   (setf (car (eat--t-term-charset eat--t-term)) charset))
 
+(defun eat--t-move-before-to-safe ()
+  "Move to a safe position before point.  Return how much moved.
+
+If the current position is safe, do nothing and return 0.
+
+Safe position is the position that's not on a multi-column wide
+character or its the internal invisible spaces."
+  (if (and (not (bobp))
+           ;; Is the current position unsafe?
+           (get-text-property (1- (point)) 'eat--t-invisible-space))
+      (let ((start-pos (point)))
+        ;; Move to the safe position.
+        (goto-char (previous-single-char-property-change
+                    (point) 'eat--t-invisible-space))
+        (cl-assert
+         (1value (or (bobp)
+                     (null (get-text-property
+                            (1- (point)) 'eat--t-invisible-space)))))
+        (- start-pos (point)))
+    0))
+
+(defun eat--t-make-pos-safe ()
+  "If the position isn't safe, make it safe by replacing with spaces."
+  (let ((moved (eat--t-move-before-to-safe)))
+    (unless (zerop moved)
+      (let ((width (get-text-property
+                    (point) 'eat--t-char-width)))
+        (cl-assert width)
+        (delete-region (point) (+ (point) width))
+        (eat--t-repeated-insert
+         ?\s width (eat--t-face-face
+                    (eat--t-term-face eat--t-term)))
+        (backward-char (- width moved))))))
+
+(defun eat--t-fix-partial-multi-col-char ()
+  "Replace any partial multi-column character with spaces."
+  (let ((face (eat--t-face-face
+               (eat--t-term-face eat--t-term))))
+    (if (get-text-property (point) 'eat--t-invisible-space)
+        (let ((start-pos (point))
+              (count nil))
+          (goto-char (next-single-char-property-change
+                      (point) 'eat--t-invisible-space))
+          (setq count (- (1+ (point)) start-pos))
+          ;; Make sure we really overwrote the character
+          ;; partially.
+          (when (< count (get-text-property
+                          (point) 'eat--t-char-width))
+            (delete-region start-pos (1+ (point)))
+            (eat--t-repeated-insert ?\s count face))
+          (goto-char start-pos))
+      ;; Detect the case where we have deleted all the invisible
+      ;; spaces before, but not the multi-column character itself.
+      (when-let* (((not (eobp)))
+                  (w (get-text-property (point) 'eat--t-char-width))
+                  ((> w 1)))
+        ;; `delete-char' also works, but it does more checks, so
+        ;; hopefully this will be faster.
+        (delete-region (point) (1+ (point)))
+        (insert (propertize " " 'face face))
+        (backward-char)))))
+
 (defun eat--t-write (str)
   "Write STR on display."
   (let* ((str
@@ -2361,35 +2423,113 @@ CHARSET should be one of `g0', `g1', `g2' and `g3'."
                s))
             (_
              str)))
+         (face (eat--t-face-face
+                (eat--t-term-face eat--t-term)))
          ;; Add `face' property.
-         (str (propertize str 'face
-                          (eat--t-face-face
-                           (eat--t-term-face eat--t-term)))))
+         (str (propertize str 'face face))
+         ;; Alist of indices and width of multi-column characters.
+         (multi-col-char-indices nil)
+         (inserted-till 0))
+    ;; Find all the multi-column wide characters in STR, using a
+    ;; binary search like algorithm; hopefully it won't slow down
+    ;; showing ASCII.
+    (named-let find ((string str)
+                     (beg 0)
+                     (end (length str)))
+      ;; NOTE: `string-width' doesn't work correctly given a range of
+      ;; characters in a string.  This workarounds the bug partially.
+      ;; FIXME: This sometimes doesn't work.  To reproduce, do C-h h
+      ;; in emacs -nw in Eat.
+      (unless (= (- end beg) (string-width string))
+        (if (= (- end beg) 1)
+            ;; Record the character width here.  We only use
+            ;; `string-width', (= `string-width' `char-width') isn't
+            ;; always t.
+            (push (cons beg (string-width string))
+                  multi-col-char-indices)
+          (let ((mid (/ (+ beg end) 2)))
+            ;; Processing the latter half first in important,
+            ;; otherwise the order of indices will be reversed.
+            (find (substring str mid end) mid end)
+            (find (substring str beg mid) beg mid)))))
     ;; TODO: Comment.
     ;; REVIEW: This probably needs to be updated.
     (let* ((disp (eat--t-term-display eat--t-term))
            (cursor (eat--t-disp-cursor disp))
            (scroll-end (eat--t-term-scroll-end eat--t-term)))
-      (while (not (string-empty-p str))
-        (let ((ins-count (min (- (eat--t-disp-width disp)
+      ;; If the position isn't safe, replace the multi-column
+      ;; character with spaces to make it safe.
+      (eat--t-make-pos-safe)
+      (while (< inserted-till (length str))
+        ;; Insert STR, and record the width of STR inserted
+        ;; successfully.
+        (let ((ins-count
+               (named-let write
+                   ((max (min (- (eat--t-disp-width disp)
                                  (1- (eat--t-cur-x cursor)))
-                              (length str))))
-          (insert (substring str 0 ins-count))
-          (setq str (substring str ins-count))
+                              (string-width str inserted-till)))
+                    (written 0))
+                 (let* ((next-multi-col (car multi-col-char-indices))
+                        (end (+ inserted-till max))
+                        (e (if next-multi-col
+                               ;; Exclude the multi-column character.
+                               (min (car next-multi-col) end)
+                             end))
+                        (wrote (- e inserted-till)))
+                   (cl-assert
+                    (= (string-width str inserted-till e)
+                       (- e inserted-till)))
+                   (insert (substring str inserted-till e))
+                   (setq inserted-till e)
+                   (if (or (null next-multi-col)
+                           (< (- end e) (cdr next-multi-col)))
+                       ;; Either everything is done, or we reached
+                       ;; the limit.
+                       (+ written wrote)
+                     ;; There are many characters which are too narrow
+                     ;; for `string-width' to return 1.  XTerm, Kitty
+                     ;; and St seems to ignore them, so we too.
+                     (if (zerop (cdr next-multi-col))
+                         (cl-incf inserted-till)
+                       (insert
+                        ;; Make sure the multi-column character
+                        ;; occupies the same number of characters as
+                        ;; its width.
+                        (propertize
+                         (make-string (1- (cdr next-multi-col)) ?\s)
+                         'invisible t 'face face
+                         'eat--t-invisible-space t
+                         'eat--t-char-width (cdr next-multi-col))
+                        ;; Now insert the multi-column character.
+                        (propertize
+                         (substring str inserted-till
+                                    (cl-incf inserted-till))
+                         'face face
+                         'eat--t-char-width (cdr next-multi-col))))
+                     (setf multi-col-char-indices
+                           (cdr multi-col-char-indices))
+                     (write (- max wrote (cdr next-multi-col))
+                            (+ written wrote
+                               (cdr next-multi-col))))))))
           (cl-incf (eat--t-cur-x cursor) ins-count)
           (if (eat--t-term-ins-mode eat--t-term)
               (delete-region
                (save-excursion
                  (eat--t-col-motion (- (eat--t-disp-width disp)
                                        (1- (eat--t-cur-x cursor))))
+                 ;; Make sure the point is safe.
+                 (eat--t-move-before-to-safe)
                  (point))
                (car (eat--t-eol)))
             (delete-region (point) (min (+ ins-count (point))
-                                        (car (eat--t-eol)))))
+                                        (car (eat--t-eol))))
+            ;; Replace any partially-overwritten character with
+            ;; spaces.
+            (eat--t-fix-partial-multi-col-char))
           (when (> (eat--t-cur-x cursor) (eat--t-disp-width disp))
             (if (not (eat--t-term-auto-margin eat--t-term))
                 (eat--t-cur-left 1)
-              (unless (string-empty-p str)
+              (when (< inserted-till (length str))
                 (when (= (eat--t-cur-y cursor) scroll-end)
                   (eat--t-scroll-up 1 'as-side-effect))
                 (if (= (eat--t-cur-y cursor) scroll-end)
@@ -2821,6 +2961,9 @@ position."
                  (max (or n 1) 1))))
     ;; Return if N is zero.
     (unless (zerop n)
+      ;; If the position isn't safe, replace the multi-column
+      ;; character with spaces to make it safe.
+      (eat--t-make-pos-safe)
       (save-excursion
         ;; Insert N spaces, with SGR background if that attribute is
         ;; set.
@@ -2829,6 +2972,8 @@ position."
         ;; Remove the characters that went beyond the edge of display.
         (eat--t-col-motion (- (eat--t-disp-width disp)
                               (+ (1- (eat--t-cur-x cursor)) n)))
+        ;; Make sure we delete any multi-column character completely.
+        (eat--t-move-before-to-safe)
         (delete-region (point) (car (eat--t-eol)))))))
 
 (defun eat--t-delete-char (n)
@@ -2844,11 +2989,16 @@ position."
                  (max (or n 1) 1))))
     ;; Return if N is zero.
     (unless (zerop n)
+      ;; If the position isn't safe, replace the multi-column
+      ;; character with spaces to make it safe.
+      (eat--t-make-pos-safe)
       (save-excursion
         (let ((m (point)))
           ;; Delete N character on current line.
           (eat--t-col-motion n)
           (delete-region m (point))
+          ;; Replace any partially-overwritten character with spaces.
+          (eat--t-fix-partial-multi-col-char)
           ;; If SGR background attribute is set, fill N characters at
           ;; the right edge of display with that background.
           (when (eat--t-face-bg face)
@@ -2878,11 +3028,16 @@ position."
                  (max (or n 1) 1))))
     ;; Return if N is zero.
     (unless (zerop n)
+      ;; If the position isn't safe, replace the multi-column
+      ;; character with spaces to make it safe.
+      (eat--t-make-pos-safe)
       (save-excursion
         (let ((m (point)))
           ;; Delete N character on current line.
           (eat--t-col-motion n)
           (delete-region m (point))
+          ;; Replace any partially-overwritten character with spaces.
+          (eat--t-fix-partial-multi-col-char)
           ;; Insert N spaces, with background if SGR background
           ;; attribute is set.
           (eat--t-repeated-insert



reply via email to

[Prev in Thread] Current Thread [Next in Thread]