mirrored from git://git.sv.gnu.org/emacs.git
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
comint.el
4407 lines (3944 loc) · 183 KB
/
comint.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
;;; comint.el --- general command interpreter in a window stuff -*- lexical-binding: t -*-
;; Copyright (C) 1988, 1990, 1992-2024 Free Software Foundation, Inc.
;; Author: Olin Shivers <shivers@cs.cmu.edu>
;; Simon Marshall <simon@gnu.org>
;; Maintainer: emacs-devel@gnu.org
;; Keywords: processes
;; Package: emacs
;; This file is part of GNU Emacs.
;; GNU Emacs 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 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This file defines a general command-interpreter-in-a-buffer package
;; (comint mode). The idea is that you can build specific process-in-a-buffer
;; modes on top of comint mode -- e.g., Lisp, shell, scheme, T, soar, ....
;; This way, all these specific packages share a common base functionality,
;; and a common set of bindings, which makes them easier to use (and
;; saves code, implementation time, etc., etc.).
;; Several packages are already defined using comint mode:
;; - shell.el defines a shell-in-a-buffer mode.
;; - cmulisp.el defines a simple lisp-in-a-buffer mode.
;;
;; - The file cmuscheme.el defines a scheme-in-a-buffer mode.
;; - The file tea.el tunes scheme and inferior-scheme modes for T.
;; - The file soar.el tunes Lisp and inferior-lisp modes for Soar.
;; - cmutex.el defines TeX and LaTeX modes that invoke TeX, LaTeX, BibTeX,
;; previewers, and printers from within Emacs.
;; - background.el allows csh-like job control inside Emacs.
;; It is pretty easy to make new derived modes for other processes.
;; For documentation on the functionality provided by Comint mode, and
;; the hooks available for customizing it, see the comments below.
;; For further information on the standard derived modes (shell,
;; inferior-lisp, inferior-scheme, ...), see the relevant source files.
;; For hints on converting existing process modes (e.g., tex-mode,
;; background, dbx, gdb, kermit, prolog, telnet) to use comint-mode
;; instead of shell-mode, see the notes at the end of this file.
;;; Brief Command Documentation:
;;============================================================================
;; Comint Mode Commands: (common to all derived modes, like shell & cmulisp
;; mode)
;;
;; M-p comint-previous-input Cycle backwards in input history
;; M-n comint-next-input Cycle forwards
;; M-r comint-history-isearch-backward-regexp Isearch input regexp backward
;; M-C-l comint-show-output Show last batch of process output
;; RET comint-send-input
;; C-d comint-delchar-or-maybe-eof Delete char unless at end of buff
;; C-c C-a comint-bol-or-process-mark First time, move point to bol;
;; second time, move to process-mark.
;; C-c C-u comint-kill-input ^u
;; C-c C-w backward-kill-word ^w
;; C-c C-c comint-interrupt-subjob ^c
;; C-c C-z comint-stop-subjob ^z
;; C-c C-\ comint-quit-subjob ^\
;; C-c C-o comint-delete-output Delete last batch of process output
;; C-c C-r comint-show-output Show last batch of process output
;; C-c C-l comint-dynamic-list-input-ring List input history
;;
;; Not bound by default in comint-mode (some are in shell mode)
;; comint-run Run a program under comint-mode
;; comint-send-invisible Read a line without echo, and send to proc
;; comint-dynamic-complete-filename Complete filename at point.
;; comint-dynamic-list-filename-completions List completions in help buffer.
;; comint-replace-by-expanded-filename Expand and complete filename at point;
;; replace with expanded/completed name.
;; comint-replace-by-expanded-history Expand history at point;
;; replace with expanded name.
;; comint-magic-space Expand history and add (a) space(s).
;; comint-kill-subjob No mercy.
;; comint-show-maximum-output Show as much output as possible.
;; comint-continue-subjob Send CONT signal to buffer's process
;; group. Useful if you accidentally
;; suspend your process (with C-c C-z).
;; comint-get-next-from-history Fetch successive input history lines
;; comint-accumulate Combine lines to send them together
;; as input.
;; comint-goto-process-mark Move point to where process-mark is.
;; comint-set-process-mark Set process-mark to point.
;; comint-mode-hook is the Comint mode hook. Basically for your keybindings.
;;; Code:
(require 'ring)
(require 'ansi-color)
(require 'ansi-osc)
(require 'regexp-opt) ;For regexp-opt-charset.
(require 'subr-x)
;;; Buffer Local Variables:
;;============================================================================
;; Comint mode buffer local variables:
;; comint-prompt-regexp string comint-bol uses to match prompt
;; comint-delimiter-argument-list list For delimiters and arguments
;; comint-last-input-start marker Handy if inferior always echoes
;; comint-last-input-end marker For comint-delete-output command
;; comint-input-ring-size integer For the input history
;; comint-input-ring ring mechanism
;; comint-input-ring-index number ...
;; comint-save-input-ring-index number ...
;; comint-input-autoexpand symbol ...
;; comint-input-ignoredups boolean ...
;; comint-dynamic-complete-functions hook For the completion mechanism
;; comint-completion-fignore list ...
;; comint-file-name-chars string ...
;; comint-file-name-quote-list list ...
;; comint-get-old-input function Hooks for specific
;; comint-input-filter-functions hook process-in-a-buffer
;; comint-output-filter-functions hook function modes.
;; comint-preoutput-filter-functions hook
;; comint-input-filter function ...
;; comint-input-sender function ...
;; comint-eol-on-send boolean ...
;; comint-process-echoes boolean ...
;; comint-scroll-to-bottom-on-input symbol For scroll behavior
;; comint-move-point-for-output symbol ...
;; comint-scroll-show-maximum-output boolean ...
;; comint-accum-marker maker For comint-accumulate
;;
;; Comint mode non-buffer local variables:
;; comint-completion-addsuffix boolean/cons For file name
;; comint-completion-autolist boolean completion behavior
;; comint-completion-recexact boolean ...
(defgroup comint nil
"General command interpreter in a window stuff."
:group 'processes)
(defgroup comint-completion nil
"Completion facilities in comint."
:group 'comint)
;; Unused.
;; (defgroup comint-source nil
;; "Source finding facilities in comint."
;; :prefix "comint-"
;; :group 'comint)
(defvar comint-prompt-regexp "^"
"Regexp to recognize prompts in the inferior process.
Defaults to \"^\", the null string at BOL.
This variable is only used if the variable
`comint-use-prompt-regexp' is non-nil.
Good choices:
Canonical Lisp: \"^[^> \\n]*>+:? *\" (Lucid, franz, kcl, T, cscheme, oaklisp)
Lucid Common Lisp: \"^\\\\(>\\\\|\\\\(->\\\\)+\\\\) *\"
franz: \"^\\\\(->\\\\|<[0-9]*>:\\\\) *\"
kcl: \"^>+ *\"
shell: \"^[^#$%>\\n]*[#$%>] *\"
T: \"^>+ *\"
This is a good thing to set in mode hooks.")
(defcustom comint-prompt-read-only nil
"If non-nil, the comint prompt is read only.
The read only region includes the newline before the prompt.
This does not affect existing prompts.
Certain derived modes may override this option.
If you set this option to t, then the safe way to temporarily
override the read-only-ness of comint prompts is to call
`comint-kill-whole-line' or `comint-kill-region' with no
narrowing in effect. This way you will be certain that none of
the remaining prompts will be accidentally messed up. You may
wish to put something like the following in your init file:
\(add-hook \\='comint-mode-hook
(lambda ()
(define-key comint-mode-map [remap kill-region] \\='comint-kill-region)
(define-key comint-mode-map [remap kill-whole-line]
\\='comint-kill-whole-line)))
If you sometimes use `comint-mode' on text-only terminals or with `emacs -nw',
you might wish to use another binding for `comint-kill-whole-line'."
:type 'boolean
:group 'comint
:version "22.1")
(defvar comint-delimiter-argument-list ()
"List of characters to recognize as separate arguments in input.
Strings comprising a character in this list will separate the arguments
surrounding them, and also be regarded as arguments in their own right (unlike
whitespace). See `comint-arguments'.
Defaults to the empty list.
For shells, a good value is (?\\| ?& ?< ?> ?\\( ?\\) ?;).
This is a good thing to set in mode hooks.")
(defcustom comint-input-autoexpand nil
"If non-nil, expand input command history references on completion.
This mirrors the optional behavior of tcsh (its autoexpand and histlist).
If the value is `input', then the expansion is seen on input.
If the value is `history', then the expansion is only when inserting
into the buffer's input ring. See also `comint-magic-space' and
`completion-at-point'.
This variable is buffer-local."
:type '(choice (const :tag "off" nil)
(const input)
(const history)
(other :tag "on" t))
:group 'comint)
(defcustom comint-highlight-input t
"If non-nil, highlight input with `comint-highlight-input' face.
Otherwise keep the original highlighting untouched."
:version "28.1"
:type 'boolean
:group 'comint)
(defface comint-highlight-input '((t (:weight bold)))
"Face to use to highlight user input."
:group 'comint)
(defface comint-highlight-prompt
'((t :inherit minibuffer-prompt))
"Face to use to highlight prompts."
:group 'comint)
(defcustom comint-input-ignoredups nil
"If non-nil, don't add input matching the last on the input ring.
This mirrors the optional behavior of bash.
This variable is buffer-local."
:type 'boolean
:group 'comint)
(defcustom comint-input-ring-file-name nil
"If non-nil, name of the file to read/write input history.
See also `comint-read-input-ring' and `comint-write-input-ring'.
`comint-mode' makes this a buffer-local variable. You probably want
to set this in a mode hook, rather than customize the default value."
:type '(choice (const :tag "Disable input history" nil)
file)
:group 'comint)
(defcustom comint-pager nil
"If non-nil, the program to use for pagination of program output.
If nil, use the default pager.
Some programs produce large amounts of output, and have provision for
pagination of their output through a filter program, commonly known as
a \"pager\". The pager limits the amount of output produced and
allows the user to interactively browse the output one page at a time.
Some programs paginate their output by default, by always starting a
pager. The program they use as the pager is specified by the
environment variable PAGER; if that variable is not defined, they use
some fixed default, such as \"less\".
The interactive browsing aspects of pagination are not needed, and get
in the way, when the output of the program is directed to an Emacs
buffer, so in those cases pagination might need to be disabled.
Disabling pagination means that some programs will produce large
amounts of output, but most such programs have other ways to limit
their output, such as additional arguments or Emacs interfaces.
To disable pagination, this variable's value should be a string that
names a program, such as \"cat\", which passes through all of the
output without any filtering or delays. Comint will then set the
PAGER variable to name that program, when it invokes external
programs."
:version "30.1"
:type '(choice (const :tag "Use default PAGER" nil)
(const :tag "Don't do paging (PAGER=cat)" "cat")
(string :tag "Program name or absolute path of pager"))
:group 'comint)
(defvar comint-input-ring-file-prefix nil
"The prefix to skip when parsing the input ring file.
This is useful in Zsh when the extended_history option is on.")
(defcustom comint-scroll-to-bottom-on-input nil
"Controls whether input to interpreter causes window to scroll.
If nil, then do not scroll. If t or `all', scroll all windows showing buffer.
If `this', scroll only the selected window.
The default is nil.
See `comint-preinput-scroll-to-bottom'. This variable is buffer-local."
:type '(choice (const :tag "off" nil)
(const t)
(const all)
(const this))
:group 'comint)
(defvaralias 'comint-scroll-to-bottom-on-output 'comint-move-point-for-output)
(defcustom comint-move-point-for-output nil
"Controls whether interpreter output moves point to the end of the output.
If nil, then output never moves point to the output.
(If the output occurs at point, it is inserted before point.)
If t or `all', move point in all windows showing the buffer.
If `this', move point only the selected window.
If `others', move point only in other windows, not in the selected window.
The default is nil.
See the variable `comint-scroll-show-maximum-output' and the function
`comint-postoutput-scroll-to-bottom'.
This variable is buffer-local in all Comint buffers."
:type '(choice (const :tag "off" nil)
(const t)
(const all)
(const this)
(const others))
:group 'comint)
(defcustom comint-move-point-for-matching-input 'after-input
"Controls where to place point after matching input.
\\<comint-mode-map>This influences the commands \\[comint-previous-matching-input-from-input] and \\[comint-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."
:version "26.1"
:type '(radio (const :tag "Stay after input" after-input)
(const :tag "Move to end of line" end-of-line))
:group 'comint)
(defcustom comint-scroll-show-maximum-output t
"Controls how to scroll due to interpreter output.
This variable applies when point is at the end of the buffer
\(either because it was originally there, or because
`comint-move-point-for-output' said to move it there)
and output from the subprocess is inserted.
Non-nil means scroll so that the window is full of text
and point is on the last line. A value of nil
means don't do anything special--scroll normally.
See also the variable `comint-move-point-for-output' and the function
`comint-postoutput-scroll-to-bottom'.
This variable is buffer-local in all Comint buffers."
:type 'boolean
:group 'comint)
(defcustom comint-buffer-maximum-size 1024
"The maximum size in lines for Comint buffers.
Comint buffers are truncated from the top to be no greater than this number, if
the function `comint-truncate-buffer' is on `comint-output-filter-functions'."
:type 'natnum
:group 'comint)
(defcustom comint-input-ring-size 500
"Size of the input history ring in `comint-mode'."
:type 'natnum
:group 'comint
:version "23.2")
(defvar comint-input-ring-separator "\n"
"Separator between commands in the history file.")
(defvar comint-input-history-ignore "^#"
"Regexp for history entries that should be ignored when Comint initializes.")
(defcustom comint-process-echoes nil
"If non-nil, assume that the subprocess echoes any input.
If so, delete one copy of the input so that only one copy eventually
appears in the buffer.
This variable is buffer-local."
:type 'boolean
:group 'comint)
;; AIX puts the name of the person being su'd to in front of the prompt.
;; kinit prints a prompt like `Password for devnull@GNU.ORG: '.
;; ksu prints a prompt like `Kerberos password for devnull/root@GNU.ORG: '.
;; ssh-add prints a prompt like `Enter passphrase: '.
;; plink prints a prompt like `Passphrase for key "root@GNU.ORG": '.
;; Ubuntu's sudo prompts like `[sudo] password for user:'
;; Some implementations of passwd use "Password (again)" as the 2nd prompt.
;; Something called "perforce" uses "Enter password:".
;; OpenVPN prints a prompt like: "Enter Auth Password:".
;; OpenBSD doas prints "doas (user@host) password:".
;; See ert test `comint-test-password-regexp'.
(defcustom comint-password-prompt-regexp
;; When extending this, please also add a corresponding test where
;; possible (see `comint-testsuite-password-strings').
(concat
"\\(^ *\\|"
(regexp-opt
'("Enter" "enter" "Enter same" "enter same" "Enter the" "enter the"
"Current"
"Enter Auth" "enter auth" "Old" "old" "New" "new" "'s" "login"
"Kerberos" "CVS" "UNIX" " SMB" "LDAP" "PEM" "SUDO"
"[sudo]" "doas" "Repeat" "Bad" "Retype" "Verify")
t)
;; Allow for user name to precede password equivalent (Bug#31075).
" +.*\\)"
"\\(?:" (regexp-opt password-word-equivalents) "\\|Response\\)"
"\\(?:\\(?:, try\\)? *again\\| (empty for no passphrase)\\| (again)\\)?"
;; "[[:alpha:]]" used to be "for", which fails to match non-English.
"\\(?: [[:alpha:]]+ .+\\)?[[:blank:]]*"
"[" (apply #'string password-colon-equivalents) "][[:space:]]*\\'"
;; The ccrypt encryption dialog doesn't end with a colon, so
;; treat it specially.
"\\|^Enter encryption key: (repeat) *\\'"
;; openssh-8.6p1 format: "(user@host) Password:".
"\\|^([^)@ \t\n]+@[^)@ \t\n]+) Password: *\\'")
"Regexp matching prompts for passwords in the inferior process.
This is used by `comint-watch-for-password-prompt'."
:version "29.1"
:type 'regexp
:group 'comint)
(defvar comint-password-prompt-max-length 256
"The maximum amount of text to examine when matching password prompts.
This is used by `comint-watch-for-password-prompt' to reduce the amount
of time spent searching for password prompts.")
;; Here are the per-interpreter hooks.
(defvar comint-get-old-input (function comint-get-old-input-default)
"Function that returns old text in Comint mode.
This function is called when return is typed while the point is in old
text. It returns the text to be submitted as process input. The
default is `comint-get-old-input-default', which either grabs the
current input field or grabs the current line and strips off leading
text matching `comint-prompt-regexp', depending on the value of
`comint-use-prompt-regexp'.")
(defvar comint-dynamic-complete-functions
'(comint-c-a-p-replace-by-expanded-history comint-filename-completion)
"List of functions called to perform completion.
Works like `completion-at-point-functions'.
See also `completion-at-point'.
This is a good thing to set in mode hooks.")
(defvar comint-input-filter #'comint-nonblank-p
"Predicate for filtering additions to input history.
Takes one argument, the input. If non-nil, the input may be saved on the input
history list. Default is to save anything that isn't all whitespace.")
(defvar comint-input-filter-functions '()
"Abnormal hook run before input is sent to the process.
These functions get one argument, a string containing the text to send.")
;;;###autoload
(defvar comint-output-filter-functions '(ansi-color-process-output comint-postoutput-scroll-to-bottom comint-watch-for-password-prompt)
"Functions to call after output is inserted into the buffer.
One possible function is `comint-postoutput-scroll-to-bottom'.
These functions get one argument, a string containing the text as originally
inserted. Note that this might not be the same as the buffer contents between
`comint-last-output-start' and the buffer's `process-mark', if other filter
functions have already modified the buffer.
See also `comint-preoutput-filter-functions'.
You can use `add-hook' to add functions to this list
either globally or locally.")
(defvar comint-input-sender-no-newline nil
"Non-nil directs the `comint-input-sender' function not to send a newline.")
(defvar comint-input-sender (function comint-simple-send)
"Function to actually send to PROCESS the STRING submitted by user.
Usually this is just `comint-simple-send', but if your mode needs to
massage the input string, put a different function here.
`comint-simple-send' just sends the string plus a newline.
\(If `comint-input-sender-no-newline' is non-nil, it omits the newline.)
This is called from the user command `comint-send-input'.")
(defcustom comint-eol-on-send t
"Non-nil means go to the end of the line before sending input.
See `comint-send-input'."
:type 'boolean
:group 'comint)
;; Note: If it is decided to purge comint-prompt-regexp from the source
;; entirely, searching for uses of this variable will help to identify
;; places that need attention.
(defcustom comint-use-prompt-regexp nil
"If non-nil, use `comint-prompt-regexp' to recognize prompts.
If nil, then program output and user-input are given different `field'
properties, which Emacs commands can use to distinguish them (in
particular, common movement commands such as `beginning-of-line'
respect field boundaries in a natural way)."
:type 'boolean
:group 'comint)
(defcustom comint-mode-hook nil
"Hook run upon entry to `comint-mode'.
This is run before the process is cranked up."
:type 'hook
:group 'comint)
(defcustom comint-exec-hook '()
"Hook run each time a process is exec'd by `comint-exec'.
This is called after the process is cranked up. It is useful for things that
must be done each time a process is executed in a Comint mode buffer (e.g.,
`set-process-query-on-exit-flag'). In contrast, `comint-mode-hook' is only
executed once, when the buffer is created."
:type 'hook
:group 'comint)
(defcustom comint-terminfo-terminal "dumb"
"Value to use for TERM when the system uses terminfo."
:type 'string
:group 'comint
:version "26.1")
(defconst comint-max-line-length
(pcase system-type
('gnu/linux 4096)
('windows-nt 8196)
(_ 1024))
"Maximum line length, in bytes, accepted by the inferior process.
This setting is only meaningful when communicating with subprocesses
via PTYs.")
(defvar comint-mode-map
(let ((map (make-sparse-keymap)))
;; Keys:
(define-key map "\ep" 'comint-previous-input)
(define-key map "\en" 'comint-next-input)
(define-key map [C-up] 'comint-previous-input)
(define-key map [C-down] 'comint-next-input)
(define-key map "\er" 'comint-history-isearch-backward-regexp)
(define-key map [?\C-c ?\M-r] 'comint-previous-matching-input-from-input)
(define-key map [?\C-c ?\M-s] 'comint-next-matching-input-from-input)
(define-key map "\e\C-l" 'comint-show-output)
(define-key map "\C-m" 'comint-send-input)
(define-key map "\C-d" 'comint-delchar-or-maybe-eof)
;; The following two are standardly bound to delete-forward-char,
;; but they should never do EOF, just delete.
(define-key map [delete] 'delete-forward-char)
(define-key map [kp-delete] 'delete-forward-char)
(define-key map "\C-c " 'comint-accumulate)
(define-key map "\C-c\C-x" 'comint-get-next-from-history)
(define-key map "\C-c\C-a" 'comint-bol-or-process-mark)
(define-key map "\C-c\C-u" 'comint-kill-input)
(define-key map "\C-c\C-w" 'backward-kill-word)
(define-key map "\C-c\C-c" 'comint-interrupt-subjob)
(define-key map "\C-c\C-z" 'comint-stop-subjob)
(define-key map "\C-c\C-\\" 'comint-quit-subjob)
(define-key map "\C-c\C-m" 'comint-copy-old-input)
(define-key map "\C-c\C-o" 'comint-delete-output)
(define-key map "\C-c\M-o" 'comint-clear-buffer)
(define-key map "\C-c\C-r" 'comint-show-output)
(define-key map "\C-c\C-e" 'comint-show-maximum-output)
(define-key map "\C-c\C-l" 'comint-dynamic-list-input-ring)
(define-key map "\C-c\C-n" 'comint-next-prompt)
(define-key map "\C-c\C-p" 'comint-previous-prompt)
(define-key map "\C-c\C-d" 'comint-send-eof)
(define-key map "\C-c\C-s" 'comint-write-output)
(define-key map "\C-c." 'comint-insert-previous-argument)
;; Mouse Buttons:
(define-key map [mouse-2] 'comint-insert-input)
;; Menu bars:
;; completion:
(define-key map [menu-bar completion]
(cons "Complete" (make-sparse-keymap "Complete")))
(define-key map [menu-bar completion complete-expand]
'("Expand File Name" . comint-replace-by-expanded-filename))
(define-key map [menu-bar completion complete-listing]
'("File Completion Listing" . comint-dynamic-list-filename-completions))
(define-key map [menu-bar completion complete-file]
'("Complete File Name" . comint-dynamic-complete-filename))
(define-key map [menu-bar completion complete]
'("Complete at Point" . completion-at-point))
;; Input history:
(define-key map [menu-bar inout]
(cons "In/Out" (make-sparse-keymap "In/Out")))
(define-key map [menu-bar inout delete-output]
'("Delete Current Output Group" . comint-delete-output))
(define-key map [menu-bar inout append-output-to-file]
'("Append Current Output Group to File" . comint-append-output-to-file))
(define-key map [menu-bar inout write-output]
'("Write Current Output Group to File" . comint-write-output))
(define-key map [menu-bar inout next-prompt]
'("Forward Output Group" . comint-next-prompt))
(define-key map [menu-bar inout previous-prompt]
'("Backward Output Group" . comint-previous-prompt))
(define-key map [menu-bar inout show-maximum-output]
'("Show Maximum Output" . comint-show-maximum-output))
(define-key map [menu-bar inout show-output]
'("Show Current Output Group" . comint-show-output))
(define-key map [menu-bar inout kill-input]
'("Kill Current Input" . comint-kill-input))
(define-key map [menu-bar inout copy-input]
'("Copy Old Input" . comint-copy-old-input))
(define-key map [menu-bar inout history-isearch-backward-regexp]
'("Isearch Input Regexp Backward..." . comint-history-isearch-backward-regexp))
(define-key map [menu-bar inout history-isearch-backward]
'("Isearch Input String Backward..." . comint-history-isearch-backward))
(define-key map [menu-bar inout forward-matching-history]
'("Forward Matching Input..." . comint-forward-matching-input))
(define-key map [menu-bar inout backward-matching-history]
'("Backward Matching Input..." . comint-backward-matching-input))
(define-key map [menu-bar inout next-matching-history]
'("Next Matching Input..." . comint-next-matching-input))
(define-key map [menu-bar inout previous-matching-history]
'("Previous Matching Input..." . comint-previous-matching-input))
(define-key map [menu-bar inout next-matching-history-from-input]
'("Next Matching Current Input" . comint-next-matching-input-from-input))
(define-key map [menu-bar inout previous-matching-history-from-input]
'("Previous Matching Current Input" . comint-previous-matching-input-from-input))
(define-key map [menu-bar inout next-history]
'("Next Input" . comint-next-input))
(define-key map [menu-bar inout previous-history]
'("Previous Input" . comint-previous-input))
(define-key map [menu-bar inout list-history]
'("List Input History" . comint-dynamic-list-input-ring))
(define-key map [menu-bar inout expand-history]
'("Expand History Before Point" . comint-replace-by-expanded-history))
;; Signals
(let ((signals-map (make-sparse-keymap "Signals")))
(define-key map [menu-bar signals] (cons "Signals" signals-map))
(define-key signals-map [eof] '("EOF" . comint-send-eof))
(define-key signals-map [kill] '("KILL" . comint-kill-subjob))
(define-key signals-map [quit] '("QUIT" . comint-quit-subjob))
(define-key signals-map [cont] '("CONT" . comint-continue-subjob))
(define-key signals-map [stop] '("STOP" . comint-stop-subjob))
(define-key signals-map [break] '("BREAK" . comint-interrupt-subjob)))
;; Put them in the menu bar:
(setq menu-bar-final-items (append '(completion inout signals)
menu-bar-final-items))
map))
(defvar-keymap comint-repeat-map
:doc "Keymap to repeat comint key sequences. Used in `repeat-mode'."
:repeat t
"C-n" #'comint-next-prompt
"C-p" #'comint-previous-prompt)
;; Fixme: Is this still relevant?
(defvar comint-ptyp t
"Non-nil if communications via pty; false if by pipe. Buffer local.
This is to work around a bug in Emacs process signaling.")
(defvar comint-input-ring nil)
(defvar comint-last-input-start nil)
(defvar comint-last-input-end nil)
(defvar comint-last-output-start nil)
(defvar comint-input-ring-index nil
"Index of last matched history element.")
(defvar comint-matching-input-from-input-string ""
"Input previously used to match input history.")
(defvar comint-save-input-ring-index nil
"Last input ring index which you copied.
This is to support the command \\[comint-get-next-from-history].")
(defvar comint-accum-marker nil
"Non-nil if you are accumulating input lines to send as input together.
The command \\[comint-accumulate] sets this.")
(defvar comint-stored-incomplete-input nil
"Stored input for history cycling.")
(put 'comint-replace-by-expanded-history 'menu-enable 'comint-input-autoexpand)
(put 'comint-input-ring 'permanent-local t)
(put 'comint-input-ring-file-name 'permanent-local t)
(put 'comint-input-ring-index 'permanent-local t)
(put 'comint-save-input-ring-index 'permanent-local t)
(put 'comint-input-autoexpand 'permanent-local t)
(put 'comint-input-filter-functions 'permanent-local t)
(put 'comint-output-filter-functions 'permanent-local t)
(put 'comint-preoutput-filter-functions 'permanent-local t)
(put 'comint-scroll-to-bottom-on-input 'permanent-local t)
(put 'comint-move-point-for-output 'permanent-local t)
(put 'comint-scroll-show-maximum-output 'permanent-local t)
(put 'comint-ptyp 'permanent-local t)
(put 'comint-mode 'mode-class 'special)
(define-derived-mode comint-mode fundamental-mode "Comint"
"Major mode for interacting with an inferior interpreter.
Interpreter name is same as buffer name, sans the asterisks.
Return at end of buffer sends line as input.
Return not at end copies rest of line to end and sends it.
Setting variable `comint-eol-on-send' means jump to the end of the line
before submitting new input.
This mode is customized to create major modes such as Inferior Lisp
mode, Shell mode, etc. This can be done by setting the hooks
`comint-input-filter-functions', `comint-input-filter', `comint-input-sender'
and `comint-get-old-input' to appropriate functions, and the variable
`comint-prompt-regexp' to the appropriate regular expression.
The mode maintains an input history of size `comint-input-ring-size'.
You can access this with the commands \\[comint-next-input],
\\[comint-previous-input], and \\[comint-dynamic-list-input-ring].
Input ring history expansion can be achieved with the commands
\\[comint-replace-by-expanded-history] or \\[comint-magic-space].
Input ring expansion is controlled by the variable `comint-input-autoexpand',
and addition is controlled by the variable `comint-input-ignoredups'.
Commands with no default key bindings include `comint-send-invisible',
`completion-at-point', `comint-dynamic-list-filename-completions', and
`comint-magic-space'.
Input to, and output from, the subprocess can cause the window to scroll to
the end of the buffer. See variables `comint-output-filter-functions',
`comint-preoutput-filter-functions', `comint-scroll-to-bottom-on-input',
and `comint-move-point-for-output'.
If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it.
\\{comint-mode-map}
Entry to this mode runs the hooks on `comint-mode-hook'."
(setq mode-line-process
(list (propertize ":%s" 'help-echo "Process status")))
(setq-local window-point-insertion-type t)
(setq-local comint-last-input-start (point-min-marker))
(setq-local comint-last-input-end (point-min-marker))
(setq-local comint-last-output-start (make-marker))
;; It is ok to let the input method edit prompt text, but RET must
;; be processed by Emacs.
(setq text-conversion-style 'action)
(make-local-variable 'comint-last-prompt)
(make-local-variable 'comint-prompt-regexp) ; Don't set; default
(make-local-variable 'comint-input-ring-size) ; ...to global val.
(make-local-variable 'comint-input-ring)
(make-local-variable 'comint-input-ring-file-name)
(or (and (boundp 'comint-input-ring) comint-input-ring)
(setq comint-input-ring (make-ring comint-input-ring-size)))
(make-local-variable 'comint-input-ring-index)
(make-local-variable 'comint-save-input-ring-index)
(or (and (boundp 'comint-input-ring-index) comint-input-ring-index)
(setq comint-input-ring-index nil))
(or (and (boundp 'comint-save-input-ring-index) comint-save-input-ring-index)
(setq comint-save-input-ring-index nil))
(make-local-variable 'comint-matching-input-from-input-string)
(make-local-variable 'comint-input-autoexpand)
(make-local-variable 'comint-input-ignoredups)
(make-local-variable 'comint-delimiter-argument-list)
(make-local-variable 'comint-completion-fignore)
(make-local-variable 'comint-get-old-input)
(make-local-variable 'comint-input-filter)
(make-local-variable 'comint-input-sender)
(make-local-variable 'comint-eol-on-send)
(make-local-variable 'comint-scroll-to-bottom-on-input)
(make-local-variable 'comint-move-point-for-output)
(make-local-variable 'comint-scroll-show-maximum-output)
(make-local-variable 'comint-stored-incomplete-input)
;; Following disabled because it seems to break the case when
;; comint-scroll-show-maximum-output is nil, and no-one can remember
;; what the original problem was. If there are problems with point
;; not going to the end, consider re-enabling this.
;; https://lists.gnu.org/r/emacs-devel/2007-08/msg00827.html
;;
;; This makes it really work to keep point at the bottom.
;; (setq-local scroll-conservatively 10000)
(add-hook 'pre-command-hook 'comint-preinput-scroll-to-bottom t t)
(make-local-variable 'comint-ptyp)
(make-local-variable 'comint-process-echoes)
(make-local-variable 'comint-file-name-chars)
(make-local-variable 'comint-file-name-quote-list)
;; dir tracking on remote files
(setq-local comint-file-name-prefix
(or (file-remote-p default-directory) ""))
(setq-local comint-accum-marker (make-marker))
(setq-local font-lock-defaults '(nil t))
(add-function :filter-return (local 'filter-buffer-substring-function)
#'comint--unmark-string-as-output)
(add-hook 'change-major-mode-hook 'font-lock-defontify nil t)
(add-hook 'isearch-mode-hook 'comint-history-isearch-setup nil t)
(add-hook 'completion-at-point-functions 'comint-completion-at-point nil t)
;; This behavior is not useful in comint buffers, and is annoying
(setq-local next-line-add-newlines nil))
(defun comint-check-proc (buffer)
"Return non-nil if there is a living process associated w/buffer BUFFER.
Living means the status is `open', `run', or `stop'.
BUFFER can be either a buffer or the name of one."
(let ((proc (get-buffer-process buffer)))
(and proc (memq (process-status proc) '(open run stop)))))
;;;###autoload
(defun make-comint-in-buffer (name buffer program &optional startfile &rest switches)
"Make a Comint process NAME in BUFFER, running PROGRAM.
If BUFFER is nil, it defaults to NAME surrounded by `*'s.
If there is a running process in BUFFER, it is not restarted.
PROGRAM should be one of the following:
- a string, denoting an executable program to create via
`start-file-process'
- a cons pair of the form (HOST . SERVICE), denoting a TCP
connection to be opened via `open-network-stream'
- nil, denoting a newly-allocated pty.
Optional fourth arg STARTFILE is the name of a file, whose
contents are sent to the process as its initial input.
If PROGRAM is a string, any more args are arguments to PROGRAM.
Return the (possibly newly created) process buffer."
(or (fboundp 'make-process)
(error "Multi-processing is not supported for this system"))
(setq buffer (get-buffer-create (or buffer (concat "*" name "*"))))
;; If no process, or nuked process, crank up a new one and put buffer in
;; comint mode. Otherwise, leave buffer and existing process alone.
(unless (comint-check-proc buffer)
(with-current-buffer buffer
(unless (derived-mode-p 'comint-mode)
(comint-mode))) ; Install local vars, mode, keymap, ...
(comint-exec buffer name program startfile switches))
buffer)
;;;###autoload
(defun make-comint (name program &optional startfile &rest switches)
"Make a Comint process NAME in a buffer, running PROGRAM.
The name of the buffer is made by surrounding NAME with `*'s.
PROGRAM should be either a string denoting an executable program to create
via `start-file-process', or a cons pair of the form (HOST . SERVICE) denoting
a TCP connection to be opened via `open-network-stream'. If there is already
a running process in that buffer, it is not restarted. Optional third arg
STARTFILE is the name of a file, whose contents are sent to the
process as its initial input.
If PROGRAM is a string, any more args are arguments to PROGRAM.
Returns the (possibly newly created) process buffer."
(apply #'make-comint-in-buffer name nil program startfile switches))
;;;###autoload
(defun comint-run (program &optional switches)
"Run PROGRAM in a Comint buffer and switch to that buffer.
If SWITCHES are supplied, they are passed to PROGRAM. With prefix argument
\\[universal-argument] prompt for SWITCHES as well as PROGRAM.
The buffer name is made by surrounding the file name of PROGRAM with `*'s.
The file name is used to make a symbol name, such as `comint-sh-hook', and any
hooks on this symbol are run in the buffer.
See `make-comint' and `comint-exec'."
(declare (interactive-only make-comint))
(interactive
(list (read-string "Run program: ")
(and (consp current-prefix-arg)
(split-string-and-unquote (read-string "Switches: ")))))
(let ((name (file-name-nondirectory program)))
(switch-to-buffer (apply #'make-comint name program nil switches))
(run-hooks (intern-soft (concat "comint-" name "-hook")))))
(defun comint-exec (buffer name command startfile switches)
"Start up a process named NAME in buffer BUFFER for Comint modes.
Runs the given COMMAND with SWITCHES, and initial input from STARTFILE.
COMMAND should be one of the following:
- a string, denoting an executable program to create via
`start-file-process'
- a cons pair of the form (HOST . SERVICE), denoting a TCP
connection to be opened via `open-network-stream'
- nil, denoting a newly-allocated pty.
This function blasts any old process running in the buffer, and
does not set the buffer mode. You can use this to cheaply run a
series of processes in the same Comint buffer. The hook
`comint-exec-hook' is run after each exec."
(with-current-buffer buffer
(let ((proc (get-buffer-process buffer))) ; Blast any old process.
(if proc (delete-process proc)))
;; Crank up a new process
(let ((proc
(if (consp command)
(open-network-stream name buffer (car command) (cdr command))
(comint-exec-1 name buffer command switches))))
(set-process-filter proc 'comint-output-filter)
(setq-local comint-ptyp process-connection-type) ; t if pty, nil if pipe.
;; Jump to the end, and set the process mark.
(goto-char (point-max))
(set-marker (process-mark proc) (point))
;; Feed it the startfile.
(when startfile
(comint-send-string proc (with-temp-buffer
(insert-file-contents startfile)
(buffer-string))))
(run-hooks 'comint-exec-hook)
buffer)))
;; This auxiliary function cranks up the process for comint-exec in
;; the appropriate environment.
(defun comint-exec-1 (name buffer command switches)
(let ((process-environment
(nconc
(comint-term-environment)
(list (format "INSIDE_EMACS=%s,comint" emacs-version))
(when comint-pager
(if (stringp comint-pager)
(list (format "PAGER=%s" comint-pager))
(error "comint-pager should be a string: %s" comint-pager)))
process-environment))
(default-directory
(if (file-accessible-directory-p default-directory)
default-directory
"/"))
proc decoding encoding changed)
(let ((exec-path (if (and command (file-name-directory command))
;; If the command has slashes, make sure we
;; first look relative to the current directory.
(cons default-directory exec-path) exec-path)))
(setq proc (apply 'start-file-process name buffer command switches)))
;; Some file name handler cannot start a process, fe ange-ftp.
(unless (processp proc) (error "No process started"))
(let ((coding-systems (process-coding-system proc)))
(setq decoding (car coding-systems)
encoding (cdr coding-systems)))
;; Even if start-file-process left the coding system for encoding data
;; sent from the process undecided, we had better use the same one
;; as what we use for decoding. But, we should suppress EOL
;; conversion.
(if (and decoding (not encoding))
(setq encoding (coding-system-change-eol-conversion decoding 'unix)
changed t))
(if changed
(set-process-coding-system proc decoding encoding))
proc))
(defun comint-term-environment ()
"Return an environment variable list for terminal configuration."
;; If using termcap, we specify `emacs' as the terminal type
;; because that lets us specify a width.
;; If using terminfo, we default to `dumb' because that is
;; a defined terminal type. `emacs' is not a defined terminal type
;; and there is no way for us to define it here.
;; Some programs that use terminfo get very confused
;; if TERM is not a valid terminal type.
(with-connection-local-variables
(if system-uses-terminfo
(list (format "TERM=%s" comint-terminfo-terminal)
"TERMCAP="
(format "COLUMNS=%d" (window-width)))
(list "TERM=emacs"
(format "TERMCAP=emacs:co#%d:tc=unknown:" (window-width))))))
(defun comint-nonblank-p (str)
"Return non-nil if STR contains non-whitespace syntax."
(not (string-match "\\`\\s *\\'" str)))
(defcustom comint-delete-old-input t
"When non-nil, delete old input on inserting previous input with \\<comint-mode-map>\\[comint-insert-input]."
:type 'boolean
:group 'comint
:version "29.1")
(defun comint-insert-input (event)
"In a Comint buffer, set the current input to the previous input at point.
If there is no previous input at point, run the command specified
by the global keymap (usually `mouse-yank-at-click')."
(interactive "e")
;; Don't set the mouse here, since it may otherwise change the behavior
;; of the command on which we fallback if there's no field at point.
;; (mouse-set-point event)
(let ((pos (posn-point (event-end event)))
field input)
(with-selected-window (posn-window (event-end event))
;; If pos is at the very end of a field, the mouse-click was
;; probably outside (to the right) of the field.
(and (< pos (field-end pos))
(< (field-end pos) (point-max))
(progn (setq field (field-at-pos pos))
(setq input (field-string-no-properties pos)))))
(if (or (null input) (null comint-accum-marker) field)
;; Fall back to the global definition if (i) the selected
;; buffer is not a comint buffer (which can happen if a
;; non-comint window was selected and we clicked in a comint
;; window), or (ii) there is no input at POS.
(let* ((keys (this-command-keys))
(last-key (and (vectorp keys) (aref keys (1- (length keys)))))
(fun (and last-key (lookup-key global-map (vector last-key)))))
(and fun (not (eq fun 'comint-insert-input))
(call-interactively fun)))
(with-selected-window (posn-window (event-end event))
;; Otherwise, insert the previous input.
(goto-char (point-max))
;; First delete any old unsent input at the end
(when comint-delete-old-input
(delete-region
(or (marker-position comint-accum-marker)
(process-mark (get-buffer-process (current-buffer))))
(point)))
;; Insert the input at point