-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patheat.el
8320 lines (7627 loc) · 328 KB
/
eat.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; eat.el --- Emulate A Terminal, in a region, in a buffer and in Eshell -*- lexical-binding: t; -*-
;; Copyright (C) 2022, 2023 Akib Azmain Turja.
;; Author: Akib Azmain Turja <akib@disroot.org>
;; Created: 2022-08-15
;; Version: 0.9.4
;; Package-Requires: ((emacs "26.1") (compat "29.1"))
;; Keywords: terminals processes
;; Homepage: https://codeberg.org/akib/emacs-eat
;; This file is not part of GNU Emacs.
;; This file 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, 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.
;; For a full copy of the GNU General Public License
;; see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Eat's name self-explanatory, it stands for "Emulate A Terminal".
;; Eat is a terminal emulator. It can run most (if not all)
;; full-screen terminal programs, including Emacs.
;; It is pretty fast, more than three times faster than Term, despite
;; being implemented entirely in Emacs Lisp. So fast that you can
;; comfortably run Emacs inside Eat, or even use your Emacs as a
;; terminal multiplexer.
;; It has many feature that other Emacs terminal emulator still don't
;; have, for example complete mouse support.
;; It flickers less than other Emacs terminal emulator, so you get
;; more performance and a smooth experience.
;; To start Eat, run M-x eat. Eat has three keybinding modes:
;; * "semi-char" mode: This is the default keybinding mode. Most
;; keys are bound to send the key to the terminal, except the
;; following keys: `C-\', `C-c', `C-x', `C-g', `C-h', `C-M-c',
;; `C-u', `M-x', `M-:', `M-!', `M-&' and some other keys (see the
;; user option `eat-semi-char-non-bound-keys' for the complete
;; list). The following special keybinding are available:
;; * `C-q': Send next key to the terminal.
;; * `C-y': Like `yank', but send the text to the terminal.
;; * `M-y': Like `yank-pop', but send the text to the terminal.
;; * `C-c' `C-k': Kill process.
;; * `C-c' `C-e': Switch to "emacs" keybinding mode.
;; * `C-c' `M-d': Switch to "char" keybinding mode.
;; * "emacs" mode: No special keybinding, except the following:
;; * `C-c' `C-j': Switch to "semi-char" keybinding mode.
;; * `C-c' `M-d': Switch to "char" keybinding mode.
;; * `C-c' `C-k': Kill process.
;; * "char" mode: All supported keys are bound to send the key to
;; the terminal, except `C-M-m' or `M-RET', which is bound to
;; switch to "semi-char" keybinding mode.
;; If you like Eshell, then there is a good news for you. Eat
;; integrates with Eshell. Eat has two global minor modes for Eshell:
;; * `eat-eshell-visual-command-mode': Run visual commands with Eat
;; instead of Term.
;; * `eat-eshell-mode': Run Eat inside Eshell. After enabling this,
;; you can run full-screen terminal programs directly in Eshell.
;; You have three keybinding modes here too, except that `C-c'
;; `C-k' is not special (i.e. not bound by Eat) in "emacs" mode
;; and "line" mode.
;;; Code:
(require 'compat)
(require 'subr-x)
(require 'cl-lib)
(require 'ansi-color)
(require 'color)
(require 'shell)
(require 'term)
(require 'url)
(require 'tramp)
(require 'term/xterm)
;; Needed by `eat-reload'.
(defvar eat--being-loaded nil
"Non-nil means Eat is being loaded.")
(setq eat--being-loaded t)
;;;; User Options.
(defgroup eat nil
"Emulate A Terminal."
:group 'processes
:group 'terminals
:link '(url-link "https://codeberg.org/akib/emacs-eat"))
(defgroup eat-term nil
"Eat terminal emulator."
:group 'eat)
(defgroup eat-ui nil
"Eat user interface."
:group 'eat)
(defgroup eat-eshell nil
"Eat Eshell integration."
:group 'eat)
(defcustom eat-default-shell-function #'eat-default-shell
"Function to call to get the default shell to run."
:type 'function
:group 'eat-ui)
(defcustom eat-shell (or explicit-shell-file-name
(getenv "ESHELL")
shell-file-name)
"Default shell to run."
:type 'string
:group 'eat-ui)
(defcustom eat-tramp-shells '(("docker" . "/bin/sh"))
"Alist specifying the shells to run in Tramp.
Each element of form (TRAMP-METHOD . SHELL), where SHELL corresponds
to the default shell for remote directories using TRAMP-METHOD."
:type '(alist :key-type string :value-type string)
:group 'eat-ui)
(defcustom eat-buffer-name "*eat*"
"The basename used for Eat buffers.
This is the default name used when running Eat."
:type 'string
:group 'eat-ui)
(defcustom eat-kill-buffer-on-exit nil
"Non-nil means automatically kill Eat buffer when process exits."
:type 'boolean
:group 'eat-ui)
(defcustom eat-term-scrollback-size 131072 ; 128 K
"Size of scrollback area in characters. nil means unlimited."
:type '(choice natnum (const nil))
:group 'eat-term
:group 'eat-ui)
(defcustom eat-enable-kill-from-terminal t
"Non-nil means allow terminal program to add text to `kill-ring'.
When non-nil, terminal program can send special escape sequence to add
some text to `kill-ring'."
:type 'boolean
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-enable-yank-to-terminal nil
"Non-nil means allow terminal program to get text from `kill-ring'.
When non-nil, terminal program can get killed text from `kill-ring'.
This is left disabled for security reasons."
:type 'boolean
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-query-before-killing-running-terminal 'auto
"Whether to query before killing running terminal.
If the value is t, always query. If the value is nil, never query.
If the value is `auto', query if a shell command is running (shell
integration needs to be enabled to use this properly)."
:type '(choice (const :tag "Yes" t)
(const :tag "No" nil)
(const :tag "If a shell command is running" auto))
:group 'eat-ui)
(defcustom eat-eshell-fallback-if-stty-not-available 'ask
"What to do if `stty' is unavailable.
`stty' is a dependency to setup terminal. If `stty' is unavailable,
Eat won't be able to setup terminal, so any input won't be visible.
The value should be any of the following:
nil Do nothing.
t Fallback to plain Eshell if `stty' is not available.
`ask' Ask what to do.
FUNCTION Call FUNCTION with the command and arguments (using
`apply') and fallback to plain Eshell if it returns
nil."
:type '(radio (const :tag "Do nothing" nil)
(const :tag "Fallback to plain Eshell" t)
(const :tag "Ask interactively" ask)
(function :tag "Function"))
:group 'eat-eshell)
(defcustom eat-sixel-scale 1.0
"Scale Sixel images by this amount."
:type 'number
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-sixel-aspect-ratio 1.0
"Aspect ratio of Sixel images.
The value is a positive number specifying the ratio of the width and
height of a Sixel pixel. For example, the value of 1.5 means the
aspect ratio of 3:2."
:type 'number
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-sixel-render-formats
'(xpm svg half-block background none)
"List of formats to render Sixel, in order of preference."
:type '(repeat (choice (const :tag "XPM Image" xpm)
(const :tag "SVG Image" svg)
(const :tag "UTF-8 half block" half-block)
(const :tag "Background color" background)
(const :tag "None" none)))
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-line-input-ring-size 1000
"Number of input history items to keep."
:type 'natnum
:group 'eat-ui)
(defcustom eat-line-auto-move-to-input t
"Non-nil means move to input line when inserting characters."
:type 'boolean
:group 'eat-ui)
(defcustom eat-line-move-point-for-matching-input 'after-input
"Controls where to place point after matching input.
\\<eat-line-mode-map>This influences the commands \
\\[eat-line-previous-matching-input-from-input] and \
\\[eat-line-next-matching-input-from-input].
If `after-input', point will be positioned after the input typed
by the user, but before the rest of the history entry that has
been inserted. If `end-of-line', point will be positioned at the
end of the current logical (not visual) line after insertion."
:type '(radio (const :tag "Stay after input" after-input)
(const :tag "Move to end of line" end-of-line))
:group 'eat-ui)
(defcustom eat-line-input-history-isearch nil
"Non-nil to Isearch in input history only, not in the terminal.
If t, usual Isearch keys like \\[isearch-backward] and \
\\[isearch-backward-regexp] in Eat buffer search in
the input history. If `dwim', Isearch keys search in the input
history only when initial point position is on input line. When
starting Isearch from other parts of the Eat buffer, they search in
the Eat buffer. If nil, Isearch operates on the whole Eat buffer."
:type '(choice (const :tag "Don't search in input history" nil)
(const :tag "When point is on input line initially, \
search history"
dwim)
(const :tag "Always search in input history" t))
:group 'eat-ui)
(defcustom eat-line-input-send-function #'eat-line-send-default
"Function to send the shell prompt input.
The function is called without any argument. The buffer is narrowed
to the input. The function may modify the input but mustn't modify
the buffer restrictions. It should call
`eat-line-send-default' to send the final output."
:type 'function
:group 'eat-ui)
(defcustom eat-semi-char-non-bound-keys
'([?\C-x] [?\C-\\] [?\C-q] [?\C-g] [?\C-h] [?\e ?\C-c] [?\C-u]
[?\e ?x] [?\e ?:] [?\e ?!] [?\e ?&]
[C-insert] [M-insert] [S-insert] [C-M-insert]
[C-S-insert] [M-S-insert] [C-M-S-insert]
[C-delete] [M-delete] [S-delete] [C-M-delete]
[C-S-delete] [M-S-delete] [C-M-S-delete]
[C-deletechar] [M-deletechar]
[S-deletechar] [C-M-deletechar] [C-S-deletechar]
[M-S-deletechar] [C-M-S-deletechar]
[C-up] [C-down] [C-right] [C-left]
[M-up] [M-down] [M-right] [M-left]
[S-up] [S-down] [S-right] [S-left]
[C-M-up] [C-M-down] [C-M-right] [C-M-left]
[C-S-up] [C-S-down] [C-S-right] [C-S-left]
[M-S-up] [M-S-down] [M-S-right] [M-S-left]
[C-M-S-up] [C-M-S-down] [C-M-S-right] [C-M-S-left]
[C-home] [M-home] [S-home] [C-M-home] [C-S-home]
[M-S-home] [C-M-S-home]
[C-end] [M-end] [S-end] [C-M-end] [C-S-end]
[M-S-end] [C-M-S-end]
[C-prior] [M-prior] [S-prior] [C-M-prior]
[C-S-prior] [M-S-prior] [C-M-S-prior]
[C-next] [M-next] [S-next] [C-M-next] [C-S-next]
[M-S-next] [C-M-S-next])
"List of keys not bound in Eat \"semi-char\" mode.
Keys appearing in this list are not bound to send the key to terminal.
Eat might still bound them to do something else (for example, changing
keybinding mode).
Each element is a vector of form [KEY] or [?\\e KEY], meaning KEY or
M-KEY shouldn't be bound. KEY shouldn't contain meta (Alt) modifier.
When changing this from Lisp, make sure to call
`eat-update-semi-char-mode-map' to update the keymap and reload Eat to
make the changes effective."
:type '(repeat sexp)
:set (lambda (sym val)
(set-default-toplevel-value sym val)
(when (and (not eat--being-loaded)
(boundp 'eat-semi-char-mode-map))
(eat-update-semi-char-mode-map)
(let ((after-load-alist nil)
(after-load-functions nil))
(eat-reload))))
:group 'eat-ui)
(defcustom eat-eshell-semi-char-non-bound-keys
'([?\C-\\] [?\C-x] [?\C-g] [?\C-h] [?\e ?\C-c] [?\C-u] [?\C-q]
[?\e ?x] [?\e ?:] [?\e ?!] [?\e ?&]
[C-insert] [M-insert] [S-insert] [C-M-insert]
[C-S-insert] [M-S-insert] [C-M-S-insert]
[C-delete] [M-delete] [S-delete] [C-M-delete]
[C-S-delete] [M-S-delete] [C-M-S-delete]
[C-deletechar] [M-deletechar]
[S-deletechar] [C-M-deletechar] [C-S-deletechar]
[M-S-deletechar] [C-M-S-deletechar]
[C-up] [C-down] [C-right] [C-left]
[M-up] [M-down] [M-right] [M-left]
[S-up] [S-down] [S-right] [S-left]
[C-M-up] [C-M-down] [C-M-right] [C-M-left]
[C-S-up] [C-S-down] [C-S-right] [C-S-left]
[M-S-up] [M-S-down] [M-S-right] [M-S-left]
[C-M-S-up] [C-M-S-down] [C-M-S-right] [C-M-S-left]
[C-home] [M-home] [S-home] [C-M-home] [C-S-home]
[M-S-home] [C-M-S-home]
[C-end] [M-end] [S-end] [C-M-end] [C-S-end]
[M-S-end] [C-M-S-end]
[C-prior] [M-prior] [S-prior] [C-M-prior]
[C-S-prior] [M-S-prior] [C-M-S-prior]
[C-next] [M-next] [S-next] [C-M-next] [C-S-next]
[M-S-next] [C-M-S-next])
"List of keys not bound in Eat-Eshell \"semi-char\" mode.
Keys appearing in this list are not bound to send the key to terminal.
Eat might still bound them to do something else (for example, changing
keybinding mode).
Each element is a vector of form [KEY] or [?\\e KEY], meaning KEY or
M-KEY shouldn't be bound. KEY shouldn't contain meta (Alt) modifier.
When changing this from Lisp, make sure to call
`eat-eshell-update-semi-char-mode-map' to update the keymap and reload
Eat to make the changes effective."
:type '(repeat sexp)
:set (lambda (sym val)
(set-default-toplevel-value sym val)
(when (and (not eat--being-loaded)
(boundp 'eat-eshell-semi-char-mode-map))
(eat-eshell-update-semi-char-mode-map)
(let ((after-load-alist nil)
(after-load-functions nil))
(eat-reload))))
:group 'eat-eshell)
(defcustom eat-enable-directory-tracking t
"Non-nil means do directory tracking.
When non-nil, Eat will track the working directory of program. You
need to configure the program to send current working directory
information. See Info node `(eat)Directory Tracking' for instructions
to setup your shell."
:type 'boolean
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-enable-shell-command-history t
"Non-nil means add shell commands to Emacs history.
When non-nil, any command you run in your shell will also appear in
the history of commands like `eat', `shell-command' and
`async-shell-command'."
:type 'boolean
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-message-handler-alist nil
"Alist of message handler name and its handler function.
The keys are the names of message handlers, and the values are their
respective handler functions.
Shells can send Eat messages, as defined in this user option. If an
appropiate message handler is defined, it's called with the other
arguments, otherwise it's ignored."
:type '(alist :key-type string
:value-type function)
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-enable-auto-line-mode nil
"Non-nil means switch to line mode automatically on shell prompt."
:type 'boolean
:group 'eat-ui)
(defcustom eat-enable-shell-prompt-annotation t
"Non-nil means annotate shell prompt with the status of command.
When non-nil, display a mark in front of shell prompt describing the
status of the command executed in that prompt."
:type 'boolean
:group 'eat-ui)
(defcustom eat-shell-prompt-annotation-position 'left-margin
"The position where to display shell prompt annotation.
The value can be one of the following:
`left-margin' Use the left margin.
`right-margin' Use the right margin."
:type '(choice (const :tag "Left margin" left-margin)
(const :tag "Right margin" right-margin))
:group 'eat-ui)
(defcustom eat-shell-prompt-annotation-running-margin-indicator "-"
"String in margin annotation to indicate the command is running."
:type 'string
:group 'eat-ui)
(defface eat-shell-prompt-annotation-running
'((t :inherit compilation-info))
"Face used in annotation to indicate the command is running."
:group 'eat-ui)
(defcustom eat-shell-prompt-annotation-success-margin-indicator "0"
"String in margin annotation to indicate the command has succeeded."
:type 'string
:group 'eat-ui)
(defface eat-shell-prompt-annotation-success
'((t :inherit success))
"Face used in annotation to indicate the command has succeeded."
:group 'eat-ui)
(defcustom eat-shell-prompt-annotation-failure-margin-indicator "X"
"String in margin annotation to indicate the command has failed."
:type 'string
:group 'eat-ui)
(defface eat-shell-prompt-annotation-failure
'((t :inherit error))
"Face used in annotation to indicate the command has failed."
:group 'eat-ui)
(defcustom eat-shell-prompt-annotation-correction-delay 0.1
"Seconds to wait before correcting shell prompt annotations.
Wait this many second after terminal update before correcting shell
prompt annotation."
:type 'number
:group 'eat-ui)
(defcustom eat-exec-hook nil
"Hook run after `eat' executes a commamnd.
The hook is run with the process run in the terminal as the only
argument."
:type 'hook
:group 'eat-ui)
(defcustom eat-update-hook nil
"Hook run after the terminal in a Eat buffer is updated."
:type 'hook
:group 'eat-ui)
(defcustom eat-exit-hook nil
"Hook run after the command executed by `eat' exits.
The hook is run with the process that just exited as the only
argument."
:type 'hook
:group 'eat-ui)
(defcustom eat-eshell-exec-hook nil
"Hook run after a terminal is created in Eshell."
:type 'hook
:group 'eat-eshell)
(defcustom eat-eshell-update-hook nil
"Hook run after the terminal in a Eshell buffer is updated."
:type 'hook
:group 'eat-eshell)
(defcustom eat-eshell-exit-hook nil
"Hook run after the terminal in Eshell is deleted."
:type 'hook
:group 'eat-eshell)
(defconst eat--cursor-type-value-type
(let ((cur-type
'(choice
(const :tag "Frame default" t)
(const :tag "Filled box" box)
(cons :tag "Box with specified size" (const box) integer)
(const :tag "Hollow cursor" hollow)
(const :tag "Vertical bar" bar)
(cons :tag "Vertical bar with specified height" (const bar)
integer)
(const :tag "Horizontal bar" hbar)
(cons :tag "Horizontal bar with specified width"
(const hbar) integer)
(const :tag "None " nil))))
`(list
,cur-type
(choice
(const :tag "No blinking" nil)
(number :tag "Blinking frequency"))
,cur-type))
"Custom type specification for Eat's cursor type variables.")
(defcustom eat-invisible-cursor-type '(nil nil nil)
"Type of cursor to use as invisible cursor in Eat buffer.
The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF).
When the cursor is on, CURSOR-ON is used as `cursor-type', which see.
BLINKING-FREQUENCY is the blinking frequency of cursor's blinking.
When the cursor is off, CURSOR-OFF is used as `cursor-type'. This
should be nil when cursor is not blinking."
:type eat--cursor-type-value-type
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-default-cursor-type
`(,(default-value 'cursor-type) nil nil)
"Cursor to use in Eat buffer.
The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF).
When the cursor is on, CURSOR-ON is used as `cursor-type', which see.
BLINKING-FREQUENCY is the blinking frequency of cursor's blinking.
When the cursor is off, CURSOR-OFF is used as `cursor-type'. This
should be nil when cursor is not blinking."
:type eat--cursor-type-value-type
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-very-visible-cursor-type
`(,(default-value 'cursor-type) 2 hollow)
"Very visible cursor to use in Eat buffer.
The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF).
When the cursor is on, CURSOR-ON is used as `cursor-type', which see.
BLINKING-FREQUENCY is the blinking frequency of cursor's blinking.
When the cursor is off, CURSOR-OFF is used as `cursor-type'. This
should be nil when cursor is not blinking."
:type eat--cursor-type-value-type
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-vertical-bar-cursor-type '(bar nil nil)
"Vertical bar cursor to use in Eat buffer.
The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF).
When the cursor is on, CURSOR-ON is used as `cursor-type', which see.
BLINKING-FREQUENCY is the blinking frequency of cursor's blinking.
When the cursor is off, CURSOR-OFF is used as `cursor-type'. This
should be nil when cursor is not blinking."
:type eat--cursor-type-value-type
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-very-visible-vertical-bar-cursor-type '(bar 2 nil)
"Very visible vertical bar cursor to use in Eat buffer.
The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF).
When the cursor is on, CURSOR-ON is used as `cursor-type', which see.
BLINKING-FREQUENCY is the blinking frequency of cursor's blinking.
When the cursor is off, CURSOR-OFF is used as `cursor-type'. This
should be nil when cursor is not blinking."
:type eat--cursor-type-value-type
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-horizontal-bar-cursor-type '(hbar nil nil)
"Horizontal bar cursor to use in Eat buffer.
The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF).
When the cursor is on, CURSOR-ON is used as `cursor-type', which see.
BLINKING-FREQUENCY is the blinking frequency of cursor's blinking.
When the cursor is off, CURSOR-OFF is used as `cursor-type'. This
should be nil when cursor is not blinking."
:type eat--cursor-type-value-type
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-very-visible-horizontal-bar-cursor-type '(hbar 2 nil)
"Very visible horizontal bar cursor to use in Eat buffer.
The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF).
When the cursor is on, CURSOR-ON is used as `cursor-type', which see.
BLINKING-FREQUENCY is the blinking frequency of cursor's blinking.
When the cursor is off, CURSOR-OFF is used as `cursor-type'. This
should be nil when cursor is not blinking."
:type eat--cursor-type-value-type
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-minimum-latency 0.008
"Minimum display latency in seconds.
Lowering it too much may cause (or increase) flickering and decrease
performance due to too many redisplays. Increasing it too much will
cause the terminal to feel less responsive. Try to increase this
value if the terminal flickers."
:type 'number
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-maximum-latency 0.033
"Minimum display latency in seconds.
Increasing it too much may make the terminal feel less responsive in
case of huge burst of output. Try to increase this value if the
terminal flickers. Try to lower the value if the terminal feels less
responsive."
:type 'number
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-term-name #'eat-term-get-suitable-term-name
"Value for the `TERM' environment variable.
The value can also be a function. In that case, the function is
called without any argument and the return value is used as the value.
For example, this can set to `eat-term-get-suitable-term-name' to set
the value according to the number of colors supported by the current
display.
This value is used by terminal programs to identify the terminal."
:type '(choice
(string :tag "Value")
(const :tag "Automatic" eat-term-get-suitable-term-name)
(function :tag "Function"))
:group 'eat-term)
;; Upgrading Eat causes `eat-term-terminfo-directory' and
;; `eat-term-shell-integration-directory' to be outdated, so update it
;; if not modified by user (or something else).
(defvar eat--load-file-path nil
"Path to currently loaded Eat.")
(defvar eat--install-path nil
"Path to directory where Eat is installed.")
(defvar eat--terminfo-path nil
"Path to directory where Terminfo databases are installed.")
(defvar eat--shell-integration-path nil
"Path to directory where shell integration scripts are installed.")
(setq eat--load-file-path (or load-file-name buffer-file-name))
(setq eat--install-path
(copy-sequence (file-name-directory eat--load-file-path)))
(defvar eat-term-terminfo-directory)
(defvar eat-term-shell-integration-directory)
(let ((old-terminfo-path eat--terminfo-path)
(old-shell-integration-path eat--shell-integration-path))
(setq eat--terminfo-path
(expand-file-name "terminfo" eat--install-path))
(setq eat--shell-integration-path
(expand-file-name "integration" eat--install-path))
(defcustom eat-term-terminfo-directory eat--terminfo-path
"Directory where required terminfo databases can be found.
This value is used by terminal programs to find the terminfo databases
that describe the capabilities of the terminal."
:type 'directory
:group 'eat-term)
(defcustom eat-term-shell-integration-directory
eat--shell-integration-path
"Directory where Eat shell integration scripts can be found.
This value is exposed to terminal programs as
`EAT_SHELL_INTEGRATION_DIR' environment variable."
:type 'directory
:group 'eat-ui
:group 'eat-eshell)
(when (eq eat-term-terminfo-directory old-terminfo-path)
(setq eat-term-terminfo-directory eat--terminfo-path))
(when (eq eat-term-shell-integration-directory
old-shell-integration-path)
(setq eat-term-shell-integration-directory
eat--shell-integration-path)))
(defcustom eat-term-inside-emacs (format "%s,eat" emacs-version)
"Value for the `INSIDE_EMACS' environment variable."
:type 'string
:group 'eat-term)
(defcustom eat-enable-blinking-text nil
"Non-nil means enable blinking of text with blink attribute.
When non-nil, enable `eat-blink-mode' to enable blinking of text with
blink attribute by default. You manually toggle `eat-blink-mode' to
toggle this behavior buffer-locally."
:type 'boolean
:group 'eat-ui
:group 'eat-eshell)
(defcustom eat-slow-blink-frequency 2
"Frequency of blinking of slowly text.
This has an effect only if `eat-blink-mode' is enabled."
:type 'number
:group 'eat-ui)
(defcustom eat-fast-blink-frequency 3
"Frequency of blinking of rapidly text.
This has an effect only if `eat-blink-mode' is enabled."
:type 'number
:group 'eat-ui)
(defcustom eat-enable-alternative-display t
"Non-nil means enable alternative display.
Full screen programs often use alternative display to keep old
contents on display unaltered."
:type 'boolean
:group 'eat-term)
(make-obsolete-variable 'eat-enable-alternative-display
"don't use it." "0.9" 'set)
(defcustom eat-enable-mouse t
"Non-nil means enable mouse support.
When non-nil, terminal programs can receive mouse events from Emacs."
:type 'boolean
:group 'eat-ui)
(defcustom eat-input-chunk-size 1024
"Maximum size of chunk of data send at once.
Long inputs send to Eat processes are broken up into chunks of this
size.
If your process is choking on big inputs, try lowering the value."
:type 'integer
:group 'eat-ui)
(defface eat-term-bold '((t :inherit bold))
"Face used to render bold text."
:group 'eat-term)
(defface eat-term-faint '((t :weight light))
"Face used to render faint text."
:group 'eat-term)
(defface eat-term-italic '((t :inherit italic))
"Face used to render italic text."
:group 'eat-term)
(defface eat-term-slow-blink '((t :inverse-video t))
"Face used to render slowly blinking text."
:group 'eat-term)
(defface eat-term-fast-blink '((t :inverse-video t))
"Face used to render rapidly blinking text."
:group 'eat-term)
;; Define color faces.
(let ((face-counter 0))
(let ((colors '("black" "red" "green" "yellow" "blue" "magenta"
"cyan" "white")))
;; Basic colors.
(dolist (color colors)
(let ((face (intern (format "eat-term-color-%i" face-counter))))
(custom-declare-face
face `((t :inherit
,(intern (format (if (eval-when-compile
(>= emacs-major-version 28))
"ansi-color-%s"
"term-color-%s")
color))))
(format "Face used to render %s color text." color)
:group 'eat-term)
(put (intern (format "eat-term-color-%s" color))
'face-alias face))
(cl-incf face-counter))
;; Bright colors.
(dolist (color colors)
(let ((face (intern (format "eat-term-color-%i" face-counter))))
(custom-declare-face
face `((t :inherit
,(intern (format (if (eval-when-compile
(>= emacs-major-version 28))
"ansi-color-bright-%s"
"term-color-%s")
color))))
(format "Face used to render bright %s color text." color)
:group 'eat-term)
(put (intern (format "eat-term-color-bright-%s" color))
'face-alias face))
(cl-incf face-counter)))
;; 256-colors.
(while (< face-counter 256)
(let ((color
(if (>= face-counter 232)
(format "#%06X"
(* #x010101
(+ 8 (* 10 (- face-counter 232)))))
(let ((col (- face-counter 16))
(res 0)
(frac (* 6 6)))
(while (<= 1 frac)
(setq res (* res #x000100))
(let ((color-num (mod (/ col frac) 6)))
(unless (zerop color-num)
(setq res (+ res #x37 (* #x28 color-num)))))
(setq frac (/ frac 6)))
(format "#%06X" res)))))
(custom-declare-face
(intern (format "eat-term-color-%i" face-counter))
`((t :foreground ,color :background ,color))
(format "Face used to render text with %i%s color of 256 color\
palette."
face-counter
(or (and (not (<= 11 (% face-counter 100) 13))
(nth (% face-counter 10)
'(nil "st" "nd" "rd")))
"th"))
:group 'eat-term))
(cl-incf face-counter)))
(defface eat-term-font-0 '((t))
"Default font."
:group 'eat-term)
(put 'eat-term-font-default 'face-alias 'eat-term-font-0)
;; Font faces, 1 to 9 (inclusive).
(cl-loop for counter from 1 to 9
do (custom-declare-face
(intern (format "eat-term-font-%i" counter)) '((t))
(format "Alternative font %i." counter)
:group 'eat-term))
;;;; Utility Functions.
(defun eat--t-goto-bol (&optional n)
"Go to the beginning of current line.
With optional argument N, go to the beginning of Nth next line if N is
positive, otherwise go to the beginning of -Nth previous line. If the
specified position is before `point-min' or after `point-max', go to
that point.
Return the number of lines moved.
Treat LINE FEED (?\\n) as the line delimiter."
;; TODO: Comment.
(setq n (or n 0))
(cond ((> n 0)
(let ((moved 0))
(while (and (< (point) (point-max))
(< moved n))
(and (search-forward "\n" nil 'move)
(cl-incf moved)))
moved))
((<= n 0)
(let ((moved 1))
(while (and (or (= moved 1)
(< (point-min) (point)))
(< n moved))
(cl-decf moved)
(and (search-backward "\n" nil 'move)
(= moved n)
(goto-char (match-end 0))))
moved))))
(defun eat--t-goto-eol (&optional n)
"Go to the end of current line.
With optional argument N, go to the end of Nth next line if N is
positive, otherwise go to the end of -Nth previous line. If the
specified position is before `point-min' or after `point-max', go to
that point.
Return the number of lines moved.
Treat LINE FEED (?\\n) as the line delimiter."
;; TODO: Comment.
(setq n (or n 0))
(cond ((>= n 0)
(let ((moved -1))
(while (and (or (= moved -1)
(< (point) (point-max)))
(< moved n))
(cl-incf moved)
(and (search-forward "\n" nil 'move)
(= moved n)
(goto-char (match-beginning 0))))
moved))
((< n 0)
(let ((moved 0))
(while (and (< (point-min) (point))
(< n moved))
(and (search-backward "\n" nil 'move)
(cl-decf moved)))
moved))))
(defun eat--t-bol (&optional n)
"Return the beginning of current line.
With optional argument N, return a cons cell whose car is the
beginning of Nth next line and cdr is N, if N is positive, otherwise
return a cons cell whose car is the beginning of -Nth previous line
and cdr is N. If the specified position is before `point-min' or
after `point-max', return a cons cell whose car is that point and cdr
is number of lines that point is away from current line.
Treat LINE FEED (?\\n) as the line delimiter."
;; Move to the beginning of line, record the point, and return that
;; point and the distance of that point from current line in lines.
(save-excursion
;; `let' is neccessary, we need to evaluate (point) after going to
;; `(eat--t-goto-bol N)'.
(let ((moved (eat--t-goto-bol n)))
(cons (point) moved))))
(defun eat--t-eol (&optional n)
"Return the end of current line.
With optional argument N, return a cons cell whose car the end of Nth
next line and cdr is N, if N is positive, otherwise return a cons cell
whose car is the end of -Nth previous line and cdr in N. If the
specified position is before `point-min' or after `point-max', return
a cons cell whose car is that point and cdr is number of lines that
point is away from current line.
Treat LINE FEED (?\\n) as the line delimiter."
;; Move to the beginning of line, record the point, and return that
;; point and the distance of that point from current line in lines.
(save-excursion
;; `let' is neccessary, we need to evaluate (point) after going to
;; (eat--t-goto-eol N).
(let ((moved (eat--t-goto-eol n)))
(cons (point) moved))))
(defun eat--t-col-motion (n)
"Move to Nth next column.
Go to Nth next column if N is positive, otherwise go to -Nth previous
column. If the specified position is before `point-min' or after
`point-max', go to that point.
Return the number of columns moved.
Assume all characters occupy a single column."
;; Record the current position.
(let ((start-pos (point)))
;; Move to the new position.
(cond ((> n 0)
(let ((eol (car (eat--t-eol)))
(pos (+ (point) n)))
(goto-char (min pos eol))))
((< n 0)
(let ((bol (car (eat--t-bol)))
(pos (+ (point) n)))
(goto-char (max pos bol)))))
;; Return the distance from the previous position.