diff options
| author | Daniel Colascione | 2025-03-02 15:53:17 -0500 |
|---|---|---|
| committer | Daniel Colascione | 2025-03-02 16:01:13 -0500 |
| commit | 13b1436d975b270cb1001cf475fa65cc854ec462 (patch) | |
| tree | e0597cd099c066b59e33fb5d1182b35b45749bbc | |
| parent | 83e2e5e24b72f7a271c04a0ce9cad1399fb2a0f9 (diff) | |
| download | emacs-dancol/term-am.tar.gz emacs-dancol/term-am.zip | |
Add auto-margin enable/disable to termdancol/term-am
* test/lisp/term-tests.el (term-line-wrap-no-auto-margins): add test
* lisp/term.el (term-auto-margins): new variable
(term-mode): documentation
(term-termcap-format): mention auto-margins flag
(term-emulate-terminal): support it
(term-reset-terminal): reset it
(term-handle-ansi-escape): notice it
* etc/e/eterm-color.ti: add auto margin capability
* etc/e/README: fix build documentation
* etc/NEWS: mention auto-margins
| -rw-r--r-- | etc/NEWS | 13 | ||||
| -rw-r--r-- | etc/e/README | 3 | ||||
| -rw-r--r-- | etc/e/eterm-color | bin | 1318 -> 1330 bytes | |||
| -rw-r--r-- | etc/e/eterm-color.ti | 6 | ||||
| -rw-r--r-- | etc/e/eterm-direct | bin | 1397 -> 1409 bytes | |||
| -rw-r--r-- | lisp/term.el | 107 | ||||
| -rw-r--r-- | test/lisp/term-tests.el | 24 |
7 files changed, 115 insertions, 38 deletions
| @@ -488,6 +488,19 @@ content. | |||
| 488 | When non-nil, buffer sizes are shown in human readable format. The | 488 | When non-nil, buffer sizes are shown in human readable format. The |
| 489 | default is nil, which retains the old format. | 489 | default is nil, which retains the old format. |
| 490 | 490 | ||
| 491 | ** Term | ||
| 492 | |||
| 493 | *** The terminal emulator now supports auto-margins control. | ||
| 494 | Term mode now handles DECAWM escape sequences that control whether text | ||
| 495 | automatically wraps at the right margin: | ||
| 496 | - \e[?7h enables auto-margins (default) | ||
| 497 | - \e[?7l disables auto-margins | ||
| 498 | |||
| 499 | When auto-margins is disabled, characters that would go beyond the right margin | ||
| 500 | are discarded, which matches the behavior of physical terminals and other | ||
| 501 | terminal emulators. Control sequences and escape sequences are still processed | ||
| 502 | correctly regardless of margin position. | ||
| 503 | |||
| 491 | ** Smerge | 504 | ** Smerge |
| 492 | 505 | ||
| 493 | *** New command 'smerge-extend' extends a conflict over surrounding lines. | 506 | *** New command 'smerge-extend' extends a conflict over surrounding lines. |
diff --git a/etc/e/README b/etc/e/README index 1293292a878..7a1a6406c7d 100644 --- a/etc/e/README +++ b/etc/e/README | |||
| @@ -7,6 +7,9 @@ version. If it is necessary, use: | |||
| 7 | 7 | ||
| 8 | tic -o ../ ./eterm-color.ti | 8 | tic -o ../ ./eterm-color.ti |
| 9 | 9 | ||
| 10 | (Sometimes tic puts output in etc/65 instead of etc/e. Move it to etc/e | ||
| 11 | yourself if it does that.) | ||
| 12 | |||
| 10 | The compiled files are used by lisp/term.el, so if they are moved, | 13 | The compiled files are used by lisp/term.el, so if they are moved, |
| 11 | term.el needs to be changed. terminfo requires them to be stored in | 14 | term.el needs to be changed. terminfo requires them to be stored in |
| 12 | an 'e' subdirectory (the first character of the file name). | 15 | an 'e' subdirectory (the first character of the file name). |
diff --git a/etc/e/eterm-color b/etc/e/eterm-color index fadac25ffcb..fb497ecca8c 100644 --- a/etc/e/eterm-color +++ b/etc/e/eterm-color | |||
| Binary files differ | |||
diff --git a/etc/e/eterm-color.ti b/etc/e/eterm-color.ti index 84b27aef5d9..fb9e59c97cf 100644 --- a/etc/e/eterm-color.ti +++ b/etc/e/eterm-color.ti | |||
| @@ -4,8 +4,8 @@ eterm-color|Emacs term.el terminal emulator term-protocol-version 0.96, | |||
| 4 | # copyright, constituting the only possible expression of the algorithm | 4 | # copyright, constituting the only possible expression of the algorithm |
| 5 | # in this format. | 5 | # in this format. |
| 6 | # | 6 | # |
| 7 | # When updating this file, etc/e/eterm-color should be regenerated by | 7 | # When updating this file, etc/e/eterm-color should be regenerated by |
| 8 | # running "make e/eterm-color" in the etc directory. | 8 | # following the instructions in etc/e/README. |
| 9 | # Any change to this file should be done at the same time with a | 9 | # Any change to this file should be done at the same time with a |
| 10 | # corresponding change to the TERMCAP environment variable in term.el. | 10 | # corresponding change to the TERMCAP environment variable in term.el. |
| 11 | # Comments in term.el specify where each of these capabilities is implemented. | 11 | # Comments in term.el specify where each of these capabilities is implemented. |
| @@ -80,6 +80,8 @@ eterm-color|Emacs term.el terminal emulator term-protocol-version 0.96, | |||
| 80 | u7=\E[6n, | 80 | u7=\E[6n, |
| 81 | smcup=\E[47h, | 81 | smcup=\E[47h, |
| 82 | rmcup=\E[47l, | 82 | rmcup=\E[47l, |
| 83 | smam=\E[?7h, | ||
| 84 | rmam=\E[?7l, | ||
| 83 | # rs2 may need to be added | 85 | # rs2 may need to be added |
| 84 | 86 | ||
| 85 | eterm-direct|Emacs term.el with direct-color indexing term-protocol-version 0.96, | 87 | eterm-direct|Emacs term.el with direct-color indexing term-protocol-version 0.96, |
diff --git a/etc/e/eterm-direct b/etc/e/eterm-direct index f4c16621eb1..945707fa6c4 100644 --- a/etc/e/eterm-direct +++ b/etc/e/eterm-direct | |||
| Binary files differ | |||
diff --git a/lisp/term.el b/lisp/term.el index 3f6b5c8f123..a74216f00c5 100644 --- a/lisp/term.el +++ b/lisp/term.el | |||
| @@ -349,6 +349,10 @@ contains saved `term-home-marker' from original sub-buffer.") | |||
| 349 | "Current vertical row (relative to home-marker) or nil if unknown.") | 349 | "Current vertical row (relative to home-marker) or nil if unknown.") |
| 350 | (defvar term-insert-mode nil) | 350 | (defvar term-insert-mode nil) |
| 351 | (defvar term-vertical-motion) | 351 | (defvar term-vertical-motion) |
| 352 | (defvar term-auto-margins t | ||
| 353 | "When non-nil, terminal will automatically wrap lines at the right margin. | ||
| 354 | This can be toggled by the application using DECAWM escape sequences.") | ||
| 355 | |||
| 352 | (defvar term-do-line-wrapping nil | 356 | (defvar term-do-line-wrapping nil |
| 353 | "Last character was a graphic in the last column. | 357 | "Last character was a graphic in the last column. |
| 354 | If next char is graphic, first move one column right | 358 | If next char is graphic, first move one column right |
| @@ -1148,6 +1152,7 @@ Entry to this mode runs the hooks on `term-mode-hook'." | |||
| 1148 | (setq-local term-last-input-start (make-marker)) | 1152 | (setq-local term-last-input-start (make-marker)) |
| 1149 | (setq-local term-last-input-end (make-marker)) | 1153 | (setq-local term-last-input-end (make-marker)) |
| 1150 | (setq-local term-last-input-match "") | 1154 | (setq-local term-last-input-match "") |
| 1155 | (setq-local term-auto-margins t) | ||
| 1151 | 1156 | ||
| 1152 | ;; Always display the onscreen keyboard. | 1157 | ;; Always display the onscreen keyboard. |
| 1153 | (setq-local touch-screen-display-keyboard t) | 1158 | (setq-local touch-screen-display-keyboard t) |
| @@ -1682,7 +1687,7 @@ Using \"emacs\" loses, because bash disables editing if $TERM == emacs.") | |||
| 1682 | :mk=\\E[8m:cb=\\E[1K:op=\\E[39;49m:Co#256:pa#32767\ | 1687 | :mk=\\E[8m:cb=\\E[1K:op=\\E[39;49m:Co#256:pa#32767\ |
| 1683 | :AB=\\E[48;5;%%dm:AF=\\E[38;5;%%dm:cr=^M\ | 1688 | :AB=\\E[48;5;%%dm:AF=\\E[38;5;%%dm:cr=^M\ |
| 1684 | :bl=^G:do=^J:le=^H:ta=^I:se=\\E[27m:ue=\\E[24m\ | 1689 | :bl=^G:do=^J:le=^H:ta=^I:se=\\E[27m:ue=\\E[24m\ |
| 1685 | :kb=^?:kD=^[[3~:sc=\\E7:rc=\\E8:r1=\\Ec:" | 1690 | :kb=^?:kD=^[[3~:sc=\\E7:rc=\\E8:r1=\\Ec:RA=\\E[?7l:SA=\\E[?7h:" |
| 1686 | ;; : -undefine ic | 1691 | ;; : -undefine ic |
| 1687 | ;; don't define :te=\\E[2J\\E[?47l\\E8:ti=\\E7\\E[?47h\ | 1692 | ;; don't define :te=\\E[2J\\E[?47l\\E8:ti=\\E7\\E[?47h\ |
| 1688 | "Termcap capabilities supported.") | 1693 | "Termcap capabilities supported.") |
| @@ -3128,19 +3133,24 @@ See `term-prompt-regexp'." | |||
| 3128 | (unless term-suppress-hard-newline | 3133 | (unless term-suppress-hard-newline |
| 3129 | (while (> (+ (length decoded-substring) old-column) | 3134 | (while (> (+ (length decoded-substring) old-column) |
| 3130 | term-width) | 3135 | term-width) |
| 3131 | (insert (substring decoded-substring 0 | 3136 | (let* ((here-length (- term-width old-column)) |
| 3132 | (- term-width old-column))) | 3137 | (to-insert (substring decoded-substring 0 here-length))) |
| 3133 | ;; Since we've enough text to fill the whole line, | 3138 | (setf decoded-substring (substring decoded-substring here-length)) |
| 3134 | ;; delete previous text regardless of | 3139 | (insert to-insert) |
| 3135 | ;; `term-insert-mode's value. | 3140 | (setf term-current-column nil) |
| 3136 | (delete-region (point) (line-end-position)) | 3141 | ;; Since we've enough text to fill the whole line, |
| 3137 | (term-down 1 t) | 3142 | ;; delete previous text regardless of |
| 3138 | (term-move-columns (- (term-current-column))) | 3143 | ;; `term-insert-mode's value. |
| 3139 | (add-text-properties (1- (point)) (point) | 3144 | (delete-region (point) (line-end-position)) |
| 3140 | '(term-line-wrap t rear-nonsticky t)) | 3145 | (if term-auto-margins |
| 3141 | (setq decoded-substring | 3146 | (progn |
| 3142 | (substring decoded-substring (- term-width old-column))) | 3147 | (term-move-to-column 0) |
| 3143 | (setq old-column 0))) | 3148 | (term-down 1 t) |
| 3149 | (add-text-properties (1- (point)) (point) | ||
| 3150 | '(term-line-wrap t rear-nonsticky t)) | ||
| 3151 | (setq old-column 0)) | ||
| 3152 | (term-move-columns -1) | ||
| 3153 | (setf old-column (term-current-column)))))) | ||
| 3144 | (insert decoded-substring) | 3154 | (insert decoded-substring) |
| 3145 | (setq term-current-column (current-column) | 3155 | (setq term-current-column (current-column) |
| 3146 | columns (- term-current-column old-column)) | 3156 | columns (- term-current-column old-column)) |
| @@ -3162,14 +3172,18 @@ See `term-prompt-regexp'." | |||
| 3162 | 3172 | ||
| 3163 | (put-text-property old-point (point) | 3173 | (put-text-property old-point (point) |
| 3164 | 'font-lock-face term-current-face)) | 3174 | 'font-lock-face term-current-face)) |
| 3165 | ;; If the last char was written in last column, | 3175 | ;; If the last char was written in last column and auto-margins is enabled, |
| 3166 | ;; back up one column, but remember we did so. | 3176 | ;; back up one column, but remember we did so. |
| 3167 | ;; Thus we emulate xterm/vt100-style line-wrapping. | 3177 | ;; Thus we emulate xterm/vt100-style line-wrapping. |
| 3178 | ;; If auto-margins is disabled, the cursor stays at the last column | ||
| 3179 | ;; and further output is discarded until a cursor movement occurs. | ||
| 3168 | (when (eq (term-current-column) term-width) | 3180 | (when (eq (term-current-column) term-width) |
| 3169 | (term-move-columns -1) | 3181 | (term-move-columns -1) |
| 3170 | ;; We check after ctrl sequence handling if point | 3182 | ;; Only set line-wrapping if auto-margins is enabled |
| 3171 | ;; was moved (and leave line-wrapping state if so). | 3183 | (when term-auto-margins |
| 3172 | (setq term-do-line-wrapping (point))) | 3184 | ;; We check after ctrl sequence handling if point |
| 3185 | ;; was moved (and leave line-wrapping state if so). | ||
| 3186 | (setq term-do-line-wrapping (point)))) | ||
| 3173 | (setq term-current-column nil) | 3187 | (setq term-current-column nil) |
| 3174 | (setq i funny)) | 3188 | (setq i funny)) |
| 3175 | (pcase-exhaustive (and (<= ctl-end str-length) (aref str i)) | 3189 | (pcase-exhaustive (and (<= ctl-end str-length) (aref str i)) |
| @@ -3205,15 +3219,19 @@ See `term-prompt-regexp'." | |||
| 3205 | ;; We only handle control sequences with a single | 3219 | ;; We only handle control sequences with a single |
| 3206 | ;; "Final" byte (see [ECMA-48] section 5.4). | 3220 | ;; "Final" byte (see [ECMA-48] section 5.4). |
| 3207 | (when (eq ctl-params-end (1- ctl-end)) | 3221 | (when (eq ctl-params-end (1- ctl-end)) |
| 3208 | (term-handle-ansi-escape | 3222 | (let* ((private (string-prefix-p "?" ctl-params)) |
| 3209 | proc | 3223 | (ctl-params |
| 3210 | (mapcar ;; We don't distinguish empty params | 3224 | (if private (substring ctl-params 1) ctl-params))) |
| 3211 | ;; from 0 (according to [ECMA-48] we | 3225 | (term-handle-ansi-escape |
| 3212 | ;; should, but all commands we support | 3226 | proc |
| 3213 | ;; default to 0 values anyway). | 3227 | (mapcar ;; We don't distinguish empty params |
| 3214 | #'string-to-number | 3228 | ;; from 0 (according to [ECMA-48] we |
| 3215 | (split-string ctl-params ";")) | 3229 | ;; should, but all commands we support |
| 3216 | (aref str (1- ctl-end))))) | 3230 | ;; default to 0 values anyway). |
| 3231 | #'string-to-number | ||
| 3232 | (split-string ctl-params ";")) | ||
| 3233 | (aref str (1- ctl-end)) | ||
| 3234 | private)))) | ||
| 3217 | (?D ;; Scroll forward (apparently not documented in | 3235 | (?D ;; Scroll forward (apparently not documented in |
| 3218 | ;; [ECMA-48], [ctlseqs] mentions it as C1 | 3236 | ;; [ECMA-48], [ctlseqs] mentions it as C1 |
| 3219 | ;; character "Index" though). | 3237 | ;; character "Index" though). |
| @@ -3426,7 +3444,8 @@ option is enabled. See `term-set-goto-process-mark'." | |||
| 3426 | (setq term-current-row 0) | 3444 | (setq term-current-row 0) |
| 3427 | (setq term-current-column 1) | 3445 | (setq term-current-column 1) |
| 3428 | (term--reset-scroll-region) | 3446 | (term--reset-scroll-region) |
| 3429 | (setq term-insert-mode nil)) | 3447 | (setq term-insert-mode nil) |
| 3448 | (setq term-auto-margins t)) | ||
| 3430 | 3449 | ||
| 3431 | (defun term--color-as-hex (for-foreground) | 3450 | (defun term--color-as-hex (for-foreground) |
| 3432 | "Return the current ANSI color as a hexadecimal color string. | 3451 | "Return the current ANSI color as a hexadecimal color string. |
| @@ -3569,8 +3588,11 @@ color is unset in the terminal state." | |||
| 3569 | ;; Handle a character assuming (eq terminal-state 2) - | 3588 | ;; Handle a character assuming (eq terminal-state 2) - |
| 3570 | ;; i.e. we have previously seen Escape followed by ?[. | 3589 | ;; i.e. we have previously seen Escape followed by ?[. |
| 3571 | 3590 | ||
| 3572 | (defun term-handle-ansi-escape (proc params char) | 3591 | (defun term-handle-ansi-escape (proc params char &optional private) |
| 3573 | (cond | 3592 | (cond |
| 3593 | ((and private (not (memq char '(?h ?l)))) | ||
| 3594 | ;; Recognize private capabilities only for mode entry and exit | ||
| 3595 | nil) | ||
| 3574 | ((or (eq char ?H) ;; cursor motion (terminfo: cup,home) | 3596 | ((or (eq char ?H) ;; cursor motion (terminfo: cup,home) |
| 3575 | ;; (eq char ?f) ;; xterm seems to handle this sequence too, not | 3597 | ;; (eq char ?f) ;; xterm seems to handle this sequence too, not |
| 3576 | ;; needed for now | 3598 | ;; needed for now |
| @@ -3633,17 +3655,30 @@ color is unset in the terminal state." | |||
| 3633 | ((eq char ?@) | 3655 | ((eq char ?@) |
| 3634 | (term-insert-spaces (max 1 (car params)))) | 3656 | (term-insert-spaces (max 1 (car params)))) |
| 3635 | ;; \E[?h - DEC Private Mode Set | 3657 | ;; \E[?h - DEC Private Mode Set |
| 3658 | |||
| 3659 | ;; N.B. we previously had a bug in which we'd decode \e[?<NR>h or | ||
| 3660 | ;; \e[?<NR>l as a command with zero in the params field and so | ||
| 3661 | ;; didn't recognize DEC private escape sequences. However, the | ||
| 3662 | ;; termcap and terminfo files had the non-? (question mark means DEC | ||
| 3663 | ;; private) versions, so things kind of worked anyway. To preserve | ||
| 3664 | ;; compatibility, we recognize both private- and non-private | ||
| 3665 | ;; messages for capabilities we added before we fixed the bug but | ||
| 3666 | ;; require the private flag for capabilities we added after. | ||
| 3636 | ((eq char ?h) | 3667 | ((eq char ?h) |
| 3637 | (cond ((eq (car params) 4) ;; (terminfo: smir) | 3668 | (cond ((eq (car params) 4) ;; (terminfo: smir) |
| 3638 | (setq term-insert-mode t)) | 3669 | (setq term-insert-mode t)) |
| 3639 | ((eq (car params) 47) ;; (terminfo: smcup) | 3670 | ((and private (eq (car params) 7)) ;; (terminfo: smam) |
| 3640 | (term-switch-to-alternate-sub-buffer t)))) | 3671 | (setq term-auto-margins t)) |
| 3672 | ((eq (car params) 47) ;; (terminfo: smcup) | ||
| 3673 | (term-switch-to-alternate-sub-buffer t)))) | ||
| 3641 | ;; \E[?l - DEC Private Mode Reset | 3674 | ;; \E[?l - DEC Private Mode Reset |
| 3642 | ((eq char ?l) | 3675 | ((eq char ?l) |
| 3643 | (cond ((eq (car params) 4) ;; (terminfo: rmir) | 3676 | (cond ((eq (car params) 4) ;; (terminfo: rmir) |
| 3644 | (setq term-insert-mode nil)) | 3677 | (setq term-insert-mode nil)) |
| 3678 | ((and private (eq (car params) 7)) ;; (terminfo: rmam) | ||
| 3679 | (setq term-auto-margins nil)) | ||
| 3645 | ((eq (car params) 47) ;; (terminfo: rmcup) | 3680 | ((eq (car params) 47) ;; (terminfo: rmcup) |
| 3646 | (term-switch-to-alternate-sub-buffer nil)))) | 3681 | (term-switch-to-alternate-sub-buffer nil)))) |
| 3647 | 3682 | ||
| 3648 | ;; Modified to allow ansi coloring -mm | 3683 | ;; Modified to allow ansi coloring -mm |
| 3649 | ;; \E[m - Set/reset modes, set bg/fg | 3684 | ;; \E[m - Set/reset modes, set bg/fg |
diff --git a/test/lisp/term-tests.el b/test/lisp/term-tests.el index 65531b66d09..5ef8c1174df 100644 --- a/test/lisp/term-tests.el +++ b/test/lisp/term-tests.el | |||
| @@ -129,6 +129,30 @@ first line\r_next line\r\n")) | |||
| 129 | (term-test-screen-from-input 40 12 (let ((str (make-string 30 ?a))) | 129 | (term-test-screen-from-input 40 12 (let ((str (make-string 30 ?a))) |
| 130 | (list str str)))))) | 130 | (list str str)))))) |
| 131 | 131 | ||
| 132 | (ert-deftest term-line-wrap-no-auto-margins () | ||
| 133 | (skip-when (memq system-type '(windows-nt ms-dos))) | ||
| 134 | (let* ((width 40) | ||
| 135 | (line (cl-loop for i upfrom 0 to 60 | ||
| 136 | collect (+ ?a (% i 26)) into chars | ||
| 137 | finally return (apply #'string chars))) | ||
| 138 | (expected (concat (substring line 0 (1- width)) | ||
| 139 | (substring line (1- (length line))))) | ||
| 140 | (rmam "\e[?7l")) | ||
| 141 | (should | ||
| 142 | (equal (term-test-screen-from-input width 12 (concat rmam line)) | ||
| 143 | expected)) | ||
| 144 | ;; Again, but split input into chunks. | ||
| 145 | (should (equal | ||
| 146 | (term-test-screen-from-input | ||
| 147 | width 12 | ||
| 148 | (cl-loop | ||
| 149 | with step = 3 | ||
| 150 | with n = (length line) | ||
| 151 | for i upfrom 0 below n by step | ||
| 152 | collect (substring line i (min n (+ i step))) into parts | ||
| 153 | finally return (cons rmam parts))) | ||
| 154 | expected)))) | ||
| 155 | |||
| 132 | (ert-deftest term-colors () | 156 | (ert-deftest term-colors () |
| 133 | (skip-when (memq system-type '(windows-nt ms-dos))) | 157 | (skip-when (memq system-type '(windows-nt ms-dos))) |
| 134 | (pcase-dolist (`(,str ,expected) ansi-test-strings) | 158 | (pcase-dolist (`(,str ,expected) ansi-test-strings) |