vterm.el (70665B)
1 ;;; vterm.el --- Fully-featured terminal emulator -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2017-2020 by Lukas Fürmetz & Contributors 4 ;; 5 ;; Author: Lukas Fürmetz <fuermetz@mailbox.org> 6 ;; Package-Version: 20241118.1627 7 ;; Package-Revision: fd5062472320 8 ;; URL: https://github.com/akermu/emacs-libvterm 9 ;; Keywords: terminals 10 ;; Package-Requires: ((emacs "25.1")) 11 12 13 ;; This file is not part of GNU Emacs. 14 15 ;; This file is free software; you can redistribute it and/or modify 16 ;; it under the terms of the GNU General Public License as published by 17 ;; the Free Software Foundation; either version 2, or (at your option) 18 ;; any later version. 19 20 ;; This file is distributed in the hope that it will be useful, 21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 ;; GNU General Public License for more details. 24 25 ;; You should have received a copy of the GNU General Public License 26 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 27 28 29 ;;; Commentary: 30 ;; 31 ;; Emacs-libvterm (vterm) is fully-fledged terminal emulator based on an 32 ;; external library (libvterm) loaded as a dynamic module. As a result of using 33 ;; compiled code (instead of elisp), emacs-libvterm is fully capable, fast, and 34 ;; it can seamlessly handle large outputs. 35 36 ;;; Installation 37 38 ;; Emacs-libvterm requires support for loading modules. You can check if your 39 ;; Emacs supports modules by inspecting the variable module-file-suffix. If it 40 ;; nil, than, you need to recompile Emacs or obtain a copy of Emacs with this 41 ;; option enabled. 42 43 ;; Emacs-libvterm requires CMake and libvterm. If libvterm is not available, 44 ;; emacs-libvterm will downloaded and compiled. In this case, libtool is 45 ;; needed. 46 47 ;; The reccomended way to install emacs-libvterm is from MELPA. 48 49 ;;; Usage 50 51 ;; To open a terminal, simply use the command M-x vterm. 52 53 ;;; Tips and tricks 54 55 ;; Adding some shell-side configuration enables a large set of additional 56 ;; features, including, directory tracking, prompt recognition, message passing. 57 58 ;;; Code: 59 60 (require 'term/xterm) 61 62 (unless module-file-suffix 63 (error "VTerm needs module support. Please compile Emacs with 64 the --with-modules option!")) 65 66 (defvar vterm-copy-mode) 67 68 ;;; Compilation of the module 69 70 (defcustom vterm-module-cmake-args "" 71 "Arguments given to CMake to compile vterm-module. 72 73 Currently, vterm defines the following flags (in addition to the 74 ones already available in CMake): 75 76 `USE_SYSTEM_LIBVTERM'. Set it to `Off' to use the vendored version of 77 libvterm instead of the one installed on your system. 78 79 This string is given verbatim to CMake, so it has to have the 80 correct syntax. An example of meaningful value for this variable 81 is `-DUSE_SYSTEM_LIBVTERM=Off'." 82 :type 'string 83 :group 'vterm) 84 85 (defcustom vterm-always-compile-module nil 86 "If not nil, if `vterm-module' is not found, compile it without asking. 87 88 When `vterm-always-compile-module' is nil, vterm will ask for 89 confirmation before compiling." 90 :type 'boolean 91 :group 'vterm) 92 93 (defvar vterm-install-buffer-name " *Install vterm* " 94 "Name of the buffer used for compiling vterm-module.") 95 96 (defun vterm-module--cmake-is-available () 97 "Return t if cmake is available. 98 CMake is needed to build vterm, here we check that we can find 99 the executable." 100 101 (unless (executable-find "cmake") 102 (error "Vterm needs CMake to be compiled. Please, install CMake")) 103 t) 104 105 ;;;###autoload 106 (defun vterm-module-compile () 107 "Compile vterm-module." 108 (interactive) 109 (when (vterm-module--cmake-is-available) 110 (let* ((vterm-directory 111 (shell-quote-argument 112 ;; NOTE: This is a workaround to fix an issue with how the Emacs 113 ;; feature/native-comp branch changes the result of 114 ;; `(locate-library "vterm")'. See emacs-devel thread 115 ;; https://lists.gnu.org/archive/html/emacs-devel/2020-07/msg00306.html 116 ;; for a discussion. 117 (file-name-directory (locate-library "vterm.el" t)))) 118 (make-commands 119 (concat 120 "cd " vterm-directory "; \ 121 mkdir -p build; \ 122 cd build; \ 123 cmake -G 'Unix Makefiles' " 124 vterm-module-cmake-args 125 " ..; \ 126 make; \ 127 cd -")) 128 (buffer (get-buffer-create vterm-install-buffer-name))) 129 (pop-to-buffer buffer) 130 (compilation-mode) 131 (if (zerop (let ((inhibit-read-only t)) 132 (call-process "sh" nil buffer t "-c" make-commands))) 133 (message "Compilation of `emacs-libvterm' module succeeded") 134 (error "Compilation of `emacs-libvterm' module failed!"))))) 135 136 ;; If the vterm-module is not compiled yet, compile it 137 (unless (require 'vterm-module nil t) 138 (if (or vterm-always-compile-module 139 (y-or-n-p "Vterm needs `vterm-module' to work. Compile it now? ")) 140 (progn 141 (vterm-module-compile) 142 (require 'vterm-module)) 143 (error "Vterm will not work until `vterm-module' is compiled!"))) 144 145 ;;; Dependencies 146 147 ;; Generate this list with: 148 ;; awk -F\" '/bind_function*/ {print "(declare-function", $2, "\"vterm-module\")"}' vterm-module.c 149 (declare-function vterm--new "vterm-module") 150 (declare-function vterm--update "vterm-module") 151 (declare-function vterm--redraw "vterm-module") 152 (declare-function vterm--write-input "vterm-module") 153 (declare-function vterm--set-size "vterm-module") 154 (declare-function vterm--set-pty-name "vterm-module") 155 (declare-function vterm--get-pwd-raw "vterm-module") 156 (declare-function vterm--reset-point "vterm-module") 157 (declare-function vterm--get-icrnl "vterm-module") 158 159 (require 'subr-x) 160 (require 'find-func) 161 (require 'cl-lib) 162 (require 'term) 163 (require 'color) 164 (require 'compile) 165 (require 'face-remap) 166 (require 'tramp) 167 (require 'bookmark) 168 169 ;;; Options 170 171 (defcustom vterm-shell shell-file-name 172 "The shell that gets run in the vterm." 173 :type 'string 174 :group 'vterm) 175 176 (defcustom vterm-tramp-shells '(("docker" "/bin/sh")) 177 "The shell that gets run in the vterm for tramp. 178 179 `vterm-tramp-shells' has to be a list of pairs of the format: 180 \(TRAMP-METHOD SHELL)" 181 :type '(alist :key-type string :value-type string) 182 :group 'vterm) 183 184 (defcustom vterm-buffer-name "*vterm*" 185 "The basename used for vterm buffers. 186 This is the default name used when running `vterm' or 187 `vterm-other-window'. 188 189 With a numeric prefix argument to `vterm', the buffer name will 190 be the value of this variable followed by the number. For 191 example, with the numeric prefix argument 2, the buffer would be 192 named \"*vterm*<2>\"." 193 :type 'string 194 :group 'vterm) 195 196 (defcustom vterm-max-scrollback 1000 197 "Maximum \\='scrollback\\=' value. 198 199 The maximum allowed is 100000. This value can modified by 200 changing the SB_MAX variable in vterm-module.h and recompiling 201 the module." 202 :type 'number 203 :group 'vterm) 204 205 (defcustom vterm-min-window-width 80 206 "Minimum window width." 207 :type 'number 208 :group 'vterm) 209 210 (defcustom vterm-kill-buffer-on-exit t 211 "If not nil vterm buffers are killed when the attached process is terminated. 212 213 If `vterm-kill-buffer-on-exit' is set to t, when the process 214 associated to a vterm buffer quits, the buffer is killed. When 215 nil, the buffer will still be available as if it were in 216 `fundamental-mode'." 217 :type 'boolean 218 :group 'vterm) 219 220 (define-obsolete-variable-alias 'vterm-clear-scrollback 221 'vterm-clear-scrollback-when-clearing "0.0.1") 222 223 (define-obsolete-variable-alias 'vterm-use-vterm-prompt 224 'vterm-use-vterm-prompt-detection-method "0.0.1") 225 226 (defcustom vterm-clear-scrollback-when-clearing nil 227 "If not nil `vterm-clear' clears both screen and scrollback. 228 229 The scrollback is everything that is not current visible on 230 screen in vterm buffers. 231 232 If `vterm-clear-scrollback-when-clearing' is nil, `vterm-clear' 233 clears only the screen, so the scrollback is accessible moving 234 the point up." 235 :type 'boolean 236 :group 'vterm) 237 238 (defcustom vterm-keymap-exceptions 239 '("C-c" "C-x" "C-u" "C-g" "C-h" "C-l" "M-x" "M-o" "C-y" "M-y") 240 "Exceptions for `vterm-keymap'. 241 242 If you use a keybinding with a prefix-key, add that prefix-key to 243 this list. Note that after doing so that prefix-key cannot be sent 244 to the terminal anymore. 245 246 The mapping is done by the macro `vterm-define-key', and the 247 function `vterm--exclude-keys' removes the keybindings defined in 248 `vterm-keymap-exceptions'." 249 :type '(repeat string) 250 :set (lambda (sym val) 251 (set sym val) 252 (when (and (fboundp 'vterm--exclude-keys) 253 (boundp 'vterm-mode-map)) 254 (vterm--exclude-keys vterm-mode-map val))) 255 :group 'vterm) 256 257 (defcustom vterm-exit-functions nil 258 "List of functions called when a vterm process exits. 259 260 Each function is called with two arguments: the vterm buffer of 261 the process if any, and a string describing the event passed from 262 the sentinel. 263 264 This hook applies only to new vterms, created after setting this 265 value with `add-hook'. 266 267 Note that this hook will not work if another package like 268 `shell-pop' sets its own sentinel to the `vterm' process." 269 :type 'hook 270 :group 'vterm) 271 272 (make-obsolete-variable 'vterm-set-title-functions 273 "This variable was substituted by `vterm-buffer-name-string'." 274 "0.0.1") 275 276 (defcustom vterm-buffer-name-string nil 277 "Format string for the title of vterm buffers. 278 279 If `vterm-buffer-name-string' is nil, vterm will not set the 280 title of its buffers. If not nil, `vterm-buffer-name-string' has 281 to be a format control string (see `format') containing one 282 instance of %s which will be substituted with the string TITLE. 283 The argument TITLE is provided by the shell. This requires shell 284 side configuration. 285 286 For example, if `vterm-buffer-name-string' is set to \"vterm %s\", 287 and the shell properly configured to set TITLE=$(pwd), than vterm 288 buffers will be named \"vterm\" followed by the current path. 289 290 See URL http://tldp.org/HOWTO/Xterm-Title-4.html for additional 291 information on the how to configure the shell." 292 :type 'string 293 :group 'vterm) 294 295 (defcustom vterm-term-environment-variable "xterm-256color" 296 "TERM value for terminal." 297 :type 'string 298 :group 'vterm) 299 300 (defcustom vterm-environment nil 301 "List of extra environment variables to the vterm shell processes only. 302 303 demo: \\='(\"env1=v1\" \"env2=v2\")" 304 :type '(repeat string) 305 :group 'vterm) 306 307 308 (defcustom vterm-enable-manipulate-selection-data-by-osc52 nil 309 "Support OSC 52 MANIPULATE SELECTION DATA(libvterm 0.2 is needed). 310 311 Support copy text to Emacs kill ring and system clipboard by using OSC 52. 312 For example: send base64 encoded \\='foo\\=' to kill ring: echo -en \\='\\e]52;c;Zm9v\\a\\=', 313 tmux can share its copy buffer to terminals by supporting osc52(like iterm2 314 xterm) you can enable this feature for tmux by : 315 set -g set-clipboard on #osc 52 copy paste share with iterm 316 set -ga terminal-overrides \\=',xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007\\=' 317 set -ga terminal-overrides \\=',screen*:XT:Ms=\\E]52;%p1%s;%p2%s\\007\\=' 318 319 The clipboard querying/clearing functionality offered by OSC 52 is not 320 implemented here,And for security reason, this feature is disabled 321 by default." 322 :type 'boolean 323 :group 'vterm) 324 325 ;; TODO: Improve doc string, it should not point to the readme but it should 326 ;; be self-contained. 327 (defcustom vterm-eval-cmds '(("find-file" find-file) 328 ("message" message) 329 ("vterm-clear-scrollback" vterm-clear-scrollback)) 330 "Whitelisted Emacs functions that can be executed from vterm. 331 332 You can execute Emacs functions directly from vterm buffers. To do this, 333 you have to escape the name of the function and its arguments with \e]51;E. 334 335 See Message passing in README. 336 337 The function you want to execute has to be in `vterm-eval-cmds'. 338 339 `vterm-eval-cmds' has to be a list of pairs of the format: 340 \(NAME-OF-COMMAND-IN-SHELL EMACS-FUNCTION) 341 342 The need for an explicit map is to avoid arbitrary code execution." 343 :type '(alist :key-type string) 344 :group 'vterm) 345 346 (defcustom vterm-disable-underline nil 347 "When not-nil, underline text properties are ignored. 348 349 This means that vterm will render underlined text as if it was not 350 underlined." 351 :type 'boolean 352 :group 'vterm) 353 354 (defcustom vterm-disable-inverse-video nil 355 "When not-nil, inverse video text properties are ignored. 356 357 This means that vterm will render reversed video text as if it was not 358 such." 359 :type 'boolean 360 :group 'vterm) 361 362 (define-obsolete-variable-alias 'vterm-disable-bold-font 363 'vterm-disable-bold "0.0.1") 364 365 (defcustom vterm-disable-bold-font nil 366 "When not-nil, bold text properties are ignored. 367 368 This means that vterm will render bold with the default face weight." 369 :type 'boolean 370 :group 'vterm) 371 372 (defcustom vterm-set-bold-hightbright nil 373 "When not-nil, using hightbright colors for bolded text, see #549." 374 :type 'boolean 375 :group 'vterm) 376 377 (defcustom vterm-ignore-blink-cursor t 378 "When t, vterm will ignore request from application to turn on/off cursor blink. 379 380 If nil, cursor in any window may begin to blink or not blink because 381 `blink-cursor-mode`is a global minor mode in Emacs, 382 you can use `M-x blink-cursor-mode` to toggle." 383 :type 'boolean 384 :group 'vterm) 385 386 (defcustom vterm-copy-exclude-prompt t 387 "When not-nil, the prompt is not included by `vterm-copy-mode-done'." 388 :type 'boolean 389 :group 'vterm) 390 391 (defcustom vterm-use-vterm-prompt-detection-method t 392 "When not-nil, the prompt is detected through the shell. 393 394 Vterm needs to know where the shell prompt is to enable all the 395 available features. There are two supported ways to do this. 396 First, the shell can inform vterm on the location of the prompt. 397 This requires shell-side configuration: the escape code 51;A is 398 used to set the current directory and prompt location. This 399 detection method is the most-reliable. To use it, you have 400 to change your shell prompt to print 51;A. 401 402 The second method is using a regular expression. This method does 403 not require any shell-side configuration. See 404 `term-prompt-regexp', for more information." 405 :type 'boolean 406 :group 'vterm) 407 408 (defcustom vterm-bookmark-check-dir t 409 "When set to non-nil, also restore directory when restoring a vterm bookmark." 410 :type 'boolean 411 :group 'vterm) 412 413 (defcustom vterm-copy-mode-remove-fake-newlines nil 414 "When not-nil fake newlines are removed on entering copy mode. 415 416 vterm inserts \\='fake\\=' newlines purely for rendering. When using 417 vterm-copy-mode these are in conflict with many emacs functions 418 like isearch-forward. if this varialbe is not-nil the 419 fake-newlines are removed on entering copy-mode and re-inserted 420 on leaving copy mode. Also truncate-lines is set to t on entering 421 copy-mode and set to nil on leaving." 422 :type 'boolean 423 :group 'vterm) 424 425 ;;; Faces 426 427 (defface vterm-color-black 428 `((t :inherit term-color-black)) 429 "Face used to render black color code." 430 :group 'vterm) 431 432 (defface vterm-color-red 433 `((t :inherit term-color-red)) 434 "Face used to render red color code." 435 :group 'vterm) 436 437 (defface vterm-color-green 438 `((t :inherit term-color-green)) 439 "Face used to render green color code." 440 :group 'vterm) 441 442 (defface vterm-color-yellow 443 `((t :inherit term-color-yellow)) 444 "Face used to render yellow color code." 445 :group 'vterm) 446 447 (defface vterm-color-blue 448 `((t :inherit term-color-blue)) 449 "Face used to render blue color code." 450 :group 'vterm) 451 452 (defface vterm-color-magenta 453 `((t :inherit term-color-magenta)) 454 "Face used to render magenta color code." 455 :group 'vterm) 456 457 (defface vterm-color-cyan 458 `((t :inherit term-color-cyan)) 459 "Face used to render cyan color code." 460 :group 'vterm) 461 462 (defface vterm-color-white 463 `((t :inherit term-color-white)) 464 "Face used to render white color code." 465 :group 'vterm) 466 467 (defface vterm-color-bright-black 468 `((t :inherit ,(if (facep 'term-color-bright-black) 469 'term-color-bright-black 470 'term-color-black))) 471 "Face used to render bright black color code." 472 :group 'vterm) 473 474 (defface vterm-color-bright-red 475 `((t :inherit ,(if (facep 'term-color-bright-red) 476 'term-color-bright-red 477 'term-color-red))) 478 "Face used to render bright red color code." 479 :group 'vterm) 480 481 (defface vterm-color-bright-green 482 `((t :inherit ,(if (facep 'term-color-bright-green) 483 'term-color-bright-green 484 'term-color-green))) 485 "Face used to render bright green color code." 486 :group 'vterm) 487 488 (defface vterm-color-bright-yellow 489 `((t :inherit ,(if (facep 'term-color-bright-yellow) 490 'term-color-bright-yellow 491 'term-color-yellow))) 492 "Face used to render bright yellow color code." 493 :group 'vterm) 494 495 (defface vterm-color-bright-blue 496 `((t :inherit ,(if (facep 'term-color-bright-blue) 497 'term-color-bright-blue 498 'term-color-blue))) 499 "Face used to render bright blue color code." 500 :group 'vterm) 501 502 (defface vterm-color-bright-magenta 503 `((t :inherit ,(if (facep 'term-color-bright-magenta) 504 'term-color-bright-magenta 505 'term-color-magenta))) 506 "Face used to render bright magenta color code." 507 :group 'vterm) 508 509 (defface vterm-color-bright-cyan 510 `((t :inherit ,(if (facep 'term-color-bright-cyan) 511 'term-color-bright-cyan 512 'term-color-cyan))) 513 "Face used to render bright cyan color code." 514 :group 'vterm) 515 516 (defface vterm-color-bright-white 517 `((t :inherit ,(if (facep 'term-color-bright-white) 518 'term-color-bright-white 519 'term-color-white))) 520 "Face used to render bright white color code." 521 :group 'vterm) 522 523 (defface vterm-color-underline 524 `((t :inherit default)) 525 "Face used to render cells with underline attribute. 526 Only foreground is used." 527 :group 'vterm) 528 529 (defface vterm-color-inverse-video 530 `((t :inherit default)) 531 "Face used to render cells with inverse video attribute. 532 Only background is used." 533 :group 'vterm) 534 535 ;;; Variables 536 537 (defvar vterm-color-palette 538 [vterm-color-black 539 vterm-color-red 540 vterm-color-green 541 vterm-color-yellow 542 vterm-color-blue 543 vterm-color-magenta 544 vterm-color-cyan 545 vterm-color-white 546 vterm-color-bright-black 547 vterm-color-bright-red 548 vterm-color-bright-green 549 vterm-color-bright-yellow 550 vterm-color-bright-blue 551 vterm-color-bright-magenta 552 vterm-color-bright-cyan 553 vterm-color-bright-white] 554 "Color palette for the foreground and background.") 555 556 (defvar-local vterm--term nil 557 "Pointer to Term.") 558 559 (defvar-local vterm--process nil 560 "Shell process of current term.") 561 562 (defvar-local vterm--redraw-timer nil) 563 (defvar-local vterm--redraw-immididately nil) 564 (defvar-local vterm--linenum-remapping nil) 565 (defvar-local vterm--prompt-tracking-enabled-p nil) 566 (defvar-local vterm--insert-function (symbol-function #'insert)) 567 (defvar-local vterm--delete-char-function (symbol-function #'delete-char)) 568 (defvar-local vterm--delete-region-function (symbol-function #'delete-region)) 569 (defvar-local vterm--undecoded-bytes nil) 570 (defvar-local vterm--copy-mode-fake-newlines nil) 571 572 573 (defvar vterm-timer-delay 0.1 574 "Delay for refreshing the buffer after receiving updates from libvterm. 575 576 A larger delary improves performance when receiving large bursts 577 of data. If nil, never delay. The units are seconds.") 578 579 ;;; Keybindings 580 581 ;; We have many functions defined by vterm-define-key. Later, we will bind some 582 ;; of the functions. If the following is not evaluated during compilation, the compiler 583 ;; will complain that some functions are not defined (eg, vterm-send-C-c) 584 (eval-and-compile 585 (defmacro vterm-define-key (key) 586 "Define a command that sends KEY with modifiers C and M to vterm." 587 (declare (indent defun) 588 (doc-string 3)) 589 `(progn (defun ,(intern (format "vterm-send-%s" key))() 590 ,(format "Sends %s to the libvterm." key) 591 (interactive) 592 (vterm-send-key ,(char-to-string (get-byte (1- (length key)) key)) 593 ,(let ((case-fold-search nil)) 594 (or (string-match-p "[A-Z]$" key) 595 (string-match-p "S-" key))) 596 ,(string-match-p "M-" key) 597 ,(string-match-p "C-" key))) 598 (make-obsolete ',(intern (format "vterm-send-%s" key)) 599 "use `vterm--self-insert' or `vterm-send' or `vterm-send-key'." 600 "v0.1"))) 601 (make-obsolete 'vterm-define-key "" "v0.1") 602 (mapc (lambda (key) 603 (eval `(vterm-define-key ,key))) 604 (cl-loop for prefix in '("M-") 605 append (cl-loop for char from ?A to ?Z 606 for key = (format "%s%c" prefix char) 607 collect key))) 608 (mapc (lambda (key) 609 (eval `(vterm-define-key ,key))) 610 (cl-loop for prefix in '("C-" "M-" "C-S-") 611 append (cl-loop for char from ?a to ?z 612 for key = (format "%s%c" prefix char) 613 collect key)))) 614 615 ;; Function keys and most of C- and M- bindings 616 (defun vterm--exclude-keys (map exceptions) 617 "Remove EXCEPTIONS from the keys bound by `vterm-define-keys'. 618 619 Exceptions are defined by `vterm-keymap-exceptions'." 620 (mapc (lambda (key) 621 (define-key map (kbd key) nil)) 622 exceptions) 623 (mapc (lambda (key) 624 (define-key map (kbd key) #'vterm--self-insert)) 625 (cl-loop for number from 1 to 12 626 for key = (format "<f%i>" number) 627 unless (member key exceptions) 628 collect key)) 629 (let ((esc-map (lookup-key map "\e")) 630 (i 0) 631 key) 632 (unless esc-map (setq esc-map (make-keymap))) 633 (while (< i 128) 634 (setq key (make-string 1 i)) 635 (unless (member (key-description key) exceptions) 636 (define-key map key 'vterm--self-insert)) 637 ;; Avoid O and [. They are used in escape sequences for various keys. 638 (unless (or (eq i ?O) (eq i 91)) 639 (unless (member (key-description key "\e") exceptions) 640 (define-key esc-map key 'vterm--self-insert-meta))) 641 (setq i (1+ i))) 642 (define-key map "\e" esc-map))) 643 644 (defun vterm-xterm-paste (event) 645 "Handle xterm paste EVENT in vterm." 646 (interactive "e") 647 (with-temp-buffer 648 (xterm-paste event) 649 (kill-new (buffer-string))) 650 (vterm-yank)) 651 652 (defvar vterm-mode-map 653 (let ((map (make-sparse-keymap))) 654 (vterm--exclude-keys map vterm-keymap-exceptions) 655 (define-key map (kbd "C-]") #'vterm--self-insert) 656 (define-key map (kbd "M-<") #'vterm--self-insert) 657 (define-key map (kbd "M->") #'vterm--self-insert) 658 (define-key map [tab] #'vterm-send-tab) 659 (define-key map (kbd "TAB") #'vterm-send-tab) 660 (define-key map [backtab] #'vterm--self-insert) 661 (define-key map [backspace] #'vterm-send-backspace) 662 (define-key map (kbd "DEL") #'vterm-send-backspace) 663 (define-key map [delete] #'vterm-send-delete) 664 (define-key map [M-backspace] #'vterm-send-meta-backspace) 665 (define-key map (kbd "M-DEL") #'vterm-send-meta-backspace) 666 (define-key map [C-backspace] #'vterm-send-meta-backspace) 667 (define-key map [return] #'vterm-send-return) 668 (define-key map (kbd "RET") #'vterm-send-return) 669 (define-key map [C-left] #'vterm--self-insert) 670 (define-key map [M-left] #'vterm--self-insert) 671 (define-key map [C-right] #'vterm--self-insert) 672 (define-key map [M-right] #'vterm--self-insert) 673 (define-key map [C-up] #'vterm--self-insert) 674 (define-key map [C-down] #'vterm--self-insert) 675 (define-key map [M-up] #'vterm--self-insert) 676 (define-key map [M-down] #'vterm--self-insert) 677 (define-key map [left] #'vterm--self-insert) 678 (define-key map [right] #'vterm--self-insert) 679 (define-key map [up] #'vterm--self-insert) 680 (define-key map [down] #'vterm--self-insert) 681 (define-key map [prior] #'vterm--self-insert) 682 (define-key map [S-prior] #'scroll-down-command) 683 (define-key map [next] #'vterm--self-insert) 684 (define-key map [S-next] #'scroll-up-command) 685 (define-key map [home] #'vterm--self-insert) 686 (define-key map [end] #'vterm--self-insert) 687 (define-key map [C-home] #'vterm--self-insert) 688 (define-key map [C-end] #'vterm--self-insert) 689 (define-key map [escape] #'vterm--self-insert) 690 (define-key map [remap yank] #'vterm-yank) 691 (define-key map [remap xterm-paste] #'vterm-xterm-paste) 692 (define-key map [remap yank-pop] #'vterm-yank-pop) 693 (define-key map [remap mouse-yank-primary] #'vterm-yank-primary) 694 (define-key map [mouse-1] #'vterm-mouse-set-point) 695 (define-key map (kbd "C-SPC") #'vterm--self-insert) 696 (define-key map (kbd "S-SPC") #'vterm-send-space) 697 (define-key map (kbd "C-_") #'vterm--self-insert) 698 (define-key map [remap undo] #'vterm-undo) 699 (define-key map (kbd "M-.") #'vterm--self-insert) 700 (define-key map (kbd "M-,") #'vterm--self-insert) 701 (define-key map (kbd "C-c C-y") #'vterm--self-insert) 702 (define-key map (kbd "C-c C-c") #'vterm--self-insert) 703 (define-key map (kbd "C-c C-l") #'vterm-clear-scrollback) 704 (define-key map (kbd "C-l") #'vterm-clear) 705 (define-key map (kbd "C-\\") #'vterm--self-insert) 706 (define-key map (kbd "C-c C-g") #'vterm--self-insert) 707 (define-key map (kbd "C-c C-u") #'vterm--self-insert) 708 (define-key map [remap self-insert-command] #'vterm--self-insert) 709 (define-key map (kbd "C-c C-r") #'vterm-reset-cursor-point) 710 (define-key map (kbd "C-c C-n") #'vterm-next-prompt) 711 (define-key map (kbd "C-c C-p") #'vterm-previous-prompt) 712 (define-key map (kbd "C-c C-t") #'vterm-copy-mode) 713 map)) 714 715 (defvar vterm-copy-mode-map 716 (let ((map (make-sparse-keymap))) 717 (define-key map (kbd "C-c C-t") #'vterm-copy-mode) 718 (define-key map [return] #'vterm-copy-mode-done) 719 (define-key map (kbd "RET") #'vterm-copy-mode-done) 720 (define-key map (kbd "C-c C-r") #'vterm-reset-cursor-point) 721 (define-key map (kbd "C-a") #'vterm-beginning-of-line) 722 (define-key map (kbd "C-e") #'vterm-end-of-line) 723 (define-key map (kbd "C-c C-n") #'vterm-next-prompt) 724 (define-key map (kbd "C-c C-p") #'vterm-previous-prompt) 725 map)) 726 727 728 ;;; Mode 729 730 (define-derived-mode vterm-mode fundamental-mode "VTerm" 731 "Major mode for vterm buffer." 732 (buffer-disable-undo) 733 (and (boundp 'display-line-numbers) 734 (let ((font-height (expt text-scale-mode-step text-scale-mode-amount))) 735 (setq vterm--linenum-remapping 736 (face-remap-add-relative 'line-number :height font-height)))) 737 (hack-dir-local-variables) 738 (let ((vterm-env (assq 'vterm-environment dir-local-variables-alist))) 739 (when vterm-env 740 (make-local-variable 'vterm-environment) 741 (setq vterm-environment (cdr vterm-env)))) 742 (let ((process-environment (append vterm-environment 743 `(,(concat "TERM=" 744 vterm-term-environment-variable) 745 ,(concat "EMACS_VTERM_PATH=" 746 (file-name-directory (find-library-name "vterm"))) 747 "INSIDE_EMACS=vterm" 748 "LINES" 749 "COLUMNS") 750 process-environment)) 751 ;; TODO: Figure out why inhibit is needed for curses to render correctly. 752 (inhibit-eol-conversion nil) 753 (coding-system-for-read 'binary) 754 (process-adaptive-read-buffering nil) 755 (width (max (- (window-max-chars-per-line) (vterm--get-margin-width)) 756 vterm-min-window-width))) 757 (setq vterm--term (vterm--new (window-body-height) 758 width vterm-max-scrollback 759 vterm-disable-bold-font 760 vterm-disable-underline 761 vterm-disable-inverse-video 762 vterm-ignore-blink-cursor 763 vterm-set-bold-hightbright)) 764 (setq buffer-read-only t) 765 (setq-local scroll-conservatively 101) 766 (setq-local scroll-margin 0) 767 (setq-local hscroll-margin 0) 768 (setq-local hscroll-step 1) 769 (setq-local truncate-lines t) 770 771 772 ;; Disable all automatic fontification 773 (setq-local font-lock-defaults '(nil t)) 774 775 (add-function :filter-return 776 (local 'filter-buffer-substring-function) 777 #'vterm--filter-buffer-substring) 778 (setq vterm--process 779 (make-process 780 :name "vterm" 781 :buffer (current-buffer) 782 :command 783 `("/bin/sh" "-c" 784 ,(format 785 "stty -nl sane %s erase ^? rows %d columns %d >/dev/null && exec %s" 786 ;; Some stty implementations (i.e. that of *BSD) do not 787 ;; support the iutf8 option. to handle that, we run some 788 ;; heuristics to work out if the system supports that 789 ;; option and set the arg string accordingly. This is a 790 ;; gross hack but FreeBSD doesn't seem to want to fix it. 791 ;; 792 ;; See: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=220009 793 (if (eq system-type 'berkeley-unix) "" "iutf8") 794 (window-body-height) 795 width (vterm--get-shell))) 796 ;; :coding 'no-conversion 797 :connection-type 'pty 798 :file-handler t 799 :filter #'vterm--filter 800 ;; The sentinel is needed if there are exit functions or if 801 ;; vterm-kill-buffer-on-exit is set to t. In this latter case, 802 ;; vterm--sentinel will kill the buffer 803 :sentinel (when (or vterm-exit-functions 804 vterm-kill-buffer-on-exit) 805 #'vterm--sentinel)))) 806 807 ;; Change major-mode is not allowed 808 ;; Vterm interfaces with an underlying process. Changing the major 809 ;; mode can break this, leading to segmentation faults. 810 (add-hook 'change-major-mode-hook 811 (lambda () (interactive) 812 (user-error "You cannot change major mode in vterm buffers")) nil t) 813 814 (vterm--set-pty-name vterm--term (process-tty-name vterm--process)) 815 (process-put vterm--process 'adjust-window-size-function 816 #'vterm--window-adjust-process-window-size) 817 818 ;; Set the truncation slot for 'buffer-display-table' to the ASCII code for a 819 ;; space character (32) to make the vterm buffer display a space instead of 820 ;; the default truncation character ($) when a line is truncated. 821 (let* ((display-table (or buffer-display-table (make-display-table)))) 822 (set-display-table-slot display-table 'truncation 32) 823 (setq buffer-display-table display-table)) 824 825 ;; Support to compilation-shell-minor-mode 826 ;; Is this necessary? See vterm--compilation-setup 827 (setq next-error-function 'vterm-next-error-function) 828 (setq-local bookmark-make-record-function 'vterm--bookmark-make-record)) 829 830 (defun vterm--get-shell () 831 "Get the shell that gets run in the vterm." 832 (if (ignore-errors (file-remote-p default-directory)) 833 (with-parsed-tramp-file-name default-directory nil 834 (or (cadr (assoc method vterm-tramp-shells)) 835 (with-connection-local-variables shell-file-name) 836 vterm-shell)) 837 vterm-shell)) 838 839 (defun vterm--bookmark-make-record () 840 "Create a vterm bookmark. 841 842 Notes down the current directory and buffer name." 843 `(nil 844 (handler . vterm--bookmark-handler) 845 (thisdir . ,default-directory) 846 (buf-name . ,(buffer-name)) 847 (defaults . nil))) 848 849 850 ;;;###autoload 851 (defun vterm--bookmark-handler (bmk) 852 "Handler to restore a vterm bookmark BMK. 853 854 If a vterm buffer of the same name does not exist, the function will create a 855 new vterm buffer of the name. It also checks the current directory and sets 856 it to the bookmarked directory if needed." 857 (let* ((thisdir (bookmark-prop-get bmk 'thisdir)) 858 (buf-name (bookmark-prop-get bmk 'buf-name)) 859 (buf (get-buffer buf-name)) 860 (thismode (and buf (with-current-buffer buf major-mode)))) 861 ;; create if no such vterm buffer exists 862 (when (or (not buf) (not (eq thismode 'vterm-mode))) 863 (setq buf (generate-new-buffer buf-name)) 864 (with-current-buffer buf 865 (when vterm-bookmark-check-dir 866 (setq default-directory thisdir)) 867 (vterm-mode))) 868 ;; check the current directory 869 (with-current-buffer buf 870 (when (and vterm-bookmark-check-dir 871 (not (string-equal default-directory thisdir))) 872 (when vterm-copy-mode 873 (vterm-copy-mode-done nil)) 874 (vterm-insert (concat "cd " thisdir)) 875 (vterm-send-return))) 876 ;; set to this vterm buf 877 (set-buffer buf))) 878 879 (defun vterm--compilation-setup () 880 "Function to enable the option `compilation-shell-minor-mode' for vterm. 881 `'compilation-shell-minor-mode' would change the value of local 882 variable `next-error-function', so we should call this function in 883 `compilation-shell-minor-mode-hook'." 884 (when (or (eq major-mode 'vterm-mode) 885 (derived-mode-p 'vterm-mode)) 886 (setq next-error-function 'vterm-next-error-function))) 887 888 (add-hook 'compilation-shell-minor-mode-hook #'vterm--compilation-setup) 889 890 ;;;###autoload 891 (defun vterm-next-error-function (n &optional reset) 892 "Advance to the next error message and visit the file where the error was. 893 This is the value of `next-error-function' in Compilation 894 buffers. Prefix arg N says how many error messages to move 895 forwards (or backwards, if negative). 896 897 Optional argument RESET clears all the errors." 898 (interactive "p") 899 (let* ((pt (point)) 900 (default-directory default-directory) 901 (pwd (vterm--get-pwd))) 902 (when pwd 903 (setq default-directory pwd)) 904 (goto-char pt) 905 (compilation-next-error-function n reset))) 906 907 ;;; Copy Mode 908 909 (defun vterm--enter-copy-mode () 910 (use-local-map nil) 911 (vterm-send-stop) 912 (when vterm-copy-mode-remove-fake-newlines 913 (save-excursion 914 (setq truncate-lines nil) 915 (vterm--remove-fake-newlines t)))) 916 917 918 (defun vterm--exit-copy-mode () 919 (when vterm-copy-mode-remove-fake-newlines 920 (save-excursion 921 (setq truncate-lines t) 922 (vterm--reinsert-fake-newlines))) 923 (vterm-reset-cursor-point) 924 (use-local-map vterm-mode-map) 925 (vterm-send-start)) 926 927 (define-minor-mode vterm-copy-mode 928 "Toggle `vterm-copy-mode'. 929 930 When `vterm-copy-mode' is enabled, the terminal will not display 931 additional output received from the underlying process and will 932 behave similarly to buffer in `fundamental-mode'. This mode is 933 typically used to copy text from vterm buffers. 934 935 A conventient way to exit `vterm-copy-mode' is with 936 `vterm-copy-mode-done', which copies the selected text and exit 937 `vterm-copy-mode'." 938 :group 'vterm 939 :lighter " VTermCopy" 940 :keymap vterm-copy-mode-map 941 (if (or (equal major-mode 'vterm-mode) 942 (derived-mode-p 'vterm-mode)) 943 (if vterm-copy-mode 944 (vterm--enter-copy-mode) 945 (vterm--exit-copy-mode)) 946 (user-error "You cannot enable vterm-copy-mode outside vterm buffers"))) 947 948 (defun vterm-copy-mode-done (arg) 949 "Save the active region or line to the kill ring and exit `vterm-copy-mode'. 950 951 If a region is defined then that region is killed, with no region then 952 current line is killed from start to end. 953 954 The option `vterm-copy-exclude-prompt' controls if the prompt 955 should be included in a line copy. Using the universal prefix ARG 956 will invert `vterm-copy-exclude-prompt' for that call." 957 (interactive "P") 958 (unless vterm-copy-mode 959 (user-error "This command is effective only in vterm-copy-mode")) 960 (unless (use-region-p) 961 (goto-char (vterm--get-beginning-of-line)) 962 ;; Are we excluding the prompt? 963 (if (or (and vterm-copy-exclude-prompt (not arg)) 964 (and (not vterm-copy-exclude-prompt) arg)) 965 (goto-char (max (or (vterm--get-prompt-point) 0) 966 (vterm--get-beginning-of-line)))) 967 (set-mark (point)) 968 (goto-char (vterm--get-end-of-line))) 969 (kill-ring-save (region-beginning) (region-end)) 970 (vterm-copy-mode -1)) 971 972 ;;; Commands 973 974 (defun vterm--self-insert-meta () 975 (interactive) 976 (when vterm--term 977 (dolist (key (vterm--translate-event-to-args 978 last-command-event :meta)) 979 (apply #'vterm-send-key key)))) 980 981 (defun vterm--self-insert () 982 "Send invoking key to libvterm." 983 (interactive) 984 (when vterm--term 985 (dolist (key (vterm--translate-event-to-args 986 last-command-event)) 987 (apply #'vterm-send-key key)))) 988 989 (defun vterm-send-key (key &optional shift meta ctrl accept-proc-output) 990 "Send KEY to libvterm with optional modifiers SHIFT, META and CTRL." 991 (deactivate-mark) 992 (when vterm--term 993 (let ((inhibit-redisplay t) 994 (inhibit-read-only t)) 995 (vterm--update vterm--term key shift meta ctrl) 996 (setq vterm--redraw-immididately t) 997 (when accept-proc-output 998 (accept-process-output vterm--process vterm-timer-delay nil t))))) 999 1000 (defun vterm-send (key) 1001 "Send KEY to libvterm. KEY can be anything `kbd' understands." 1002 (dolist (key (vterm--translate-event-to-args 1003 (listify-key-sequence (kbd key)))) 1004 (apply #'vterm-send-key key))) 1005 1006 (defun vterm-send-next-key () 1007 "Read next input event and send it to the libvterm. 1008 1009 With this you can directly send modified keys to applications 1010 running in the terminal (like Emacs or Nano)." 1011 (interactive) 1012 (dolist (key (vterm--translate-event-to-args 1013 (read-event))) 1014 (apply #'vterm-send-key key))) 1015 1016 (defun vterm-send-start () 1017 "Output from the system is started when the system receives START." 1018 (interactive) 1019 (vterm-send-key "<start>")) 1020 1021 (defun vterm-send-stop () 1022 "Output from the system is stopped when the system receives STOP." 1023 (interactive) 1024 (vterm-send-key "<stop>")) 1025 1026 (defun vterm-send-return () 1027 "Send `C-m' to the libvterm." 1028 (interactive) 1029 (deactivate-mark) 1030 (when vterm--term 1031 (if (vterm--get-icrnl vterm--term) 1032 (process-send-string vterm--process "\C-j") 1033 (process-send-string vterm--process "\C-m")))) 1034 1035 (defun vterm-send-tab () 1036 "Send `<tab>' to the libvterm." 1037 (interactive) 1038 (vterm-send-key "<tab>")) 1039 1040 (defun vterm-send-space () 1041 "Send `<space>' to the libvterm." 1042 (interactive) 1043 (vterm-send-key " ")) 1044 1045 (defun vterm-send-backspace () 1046 "Send `<backspace>' to the libvterm." 1047 (interactive) 1048 (vterm-send-key "<backspace>")) 1049 1050 (defun vterm-send-delete () 1051 "Send `<delete>' to the libvterm." 1052 (interactive) 1053 (vterm-send-key "<delete>")) 1054 1055 (defun vterm-send-meta-backspace () 1056 "Send `M-<backspace>' to the libvterm." 1057 (interactive) 1058 (vterm-send-key "<backspace>" nil t)) 1059 1060 (defun vterm-send-up () 1061 "Send `<up>' to the libvterm." 1062 (interactive) 1063 (vterm-send-key "<up>")) 1064 (make-obsolete 'vterm-send-up 'vterm--self-insert "v0.1") 1065 1066 (defun vterm-send-down () 1067 "Send `<down>' to the libvterm." 1068 (interactive) 1069 (vterm-send-key "<down>")) 1070 (make-obsolete 'vterm-send-down 'vterm--self-insert "v0.1") 1071 1072 (defun vterm-send-left () 1073 "Send `<left>' to the libvterm." 1074 (interactive) 1075 (vterm-send-key "<left>")) 1076 (make-obsolete 'vterm-send-left 'vterm--self-insert "v0.1") 1077 1078 (defun vterm-send-right () 1079 "Send `<right>' to the libvterm." 1080 (interactive) 1081 (vterm-send-key "<right>")) 1082 (make-obsolete 'vterm-send-right 'vterm--self-insert "v0.1") 1083 1084 (defun vterm-send-prior () 1085 "Send `<prior>' to the libvterm." 1086 (interactive) 1087 (vterm-send-key "<prior>")) 1088 (make-obsolete 'vterm-send-prior 'vterm--self-insert "v0.1") 1089 1090 (defun vterm-send-next () 1091 "Send `<next>' to the libvterm." 1092 (interactive) 1093 (vterm-send-key "<next>")) 1094 (make-obsolete 'vterm-send-next 'vterm--self-insert "v0.1") 1095 1096 (defun vterm-send-meta-dot () 1097 "Send `M-.' to the libvterm." 1098 (interactive) 1099 (vterm-send-key "." nil t)) 1100 (make-obsolete 'vterm-send-meta-dot 'vterm--self-insert "v0.1") 1101 1102 (defun vterm-send-meta-comma () 1103 "Send `M-,' to the libvterm." 1104 (interactive) 1105 (vterm-send-key "," nil t)) 1106 (make-obsolete 'vterm-send-meta-comma 'vterm--self-insert "v0.1") 1107 1108 (defun vterm-send-ctrl-slash () 1109 "Send `C-\' to the libvterm." 1110 (interactive) 1111 (vterm-send-key "\\" nil nil t)) 1112 (make-obsolete 'vterm-send-ctrl-slash 'vterm--self-insert "v0.1") 1113 1114 (defun vterm-send-escape () 1115 "Send `<escape>' to the libvterm." 1116 (interactive) 1117 (vterm-send-key "<escape>")) 1118 1119 (defun vterm-clear-scrollback () 1120 "Send `<clear-scrollback>' to the libvterm." 1121 (interactive) 1122 (vterm-send-key "<clear_scrollback>")) 1123 1124 (defun vterm-clear (&optional arg) 1125 "Send `<clear>' to the libvterm. 1126 1127 `vterm-clear-scrollback' determines whether 1128 `vterm-clear' should also clear the scrollback or not. 1129 1130 This behavior can be altered by calling `vterm-clear' with a 1131 prefix argument ARG or with \\[universal-argument]." 1132 (interactive "P") 1133 (if (or 1134 (and vterm-clear-scrollback-when-clearing (not arg)) 1135 (and arg (not vterm-clear-scrollback-when-clearing))) 1136 (vterm-clear-scrollback)) 1137 (vterm-send-key "l" nil nil :ctrl)) 1138 1139 (defun vterm-undo () 1140 "Send `C-_' to the libvterm." 1141 (interactive) 1142 (vterm-send-key "_" nil nil t)) 1143 1144 (defun vterm-yank (&optional arg) 1145 "Yank (paste) text in vterm. 1146 1147 Argument ARG is passed to `yank'." 1148 (interactive "P") 1149 (deactivate-mark) 1150 (vterm-goto-char (point)) 1151 (let ((inhibit-read-only t)) 1152 (cl-letf (((symbol-function 'insert-for-yank) #'vterm-insert)) 1153 (yank arg)))) 1154 1155 (defun vterm-yank-primary () 1156 "Yank text from the primary selection in vterm." 1157 (interactive) 1158 (vterm-goto-char (point)) 1159 (let ((inhibit-read-only t) 1160 (primary (gui-get-primary-selection))) 1161 (cl-letf (((symbol-function 'insert-for-yank) #'vterm-insert)) 1162 (insert-for-yank primary)))) 1163 1164 (defun vterm-yank-pop (&optional arg) 1165 "Replaced text just yanked with the next entry in the kill ring. 1166 1167 Argument ARG is passed to `yank'" 1168 (interactive "p") 1169 (vterm-goto-char (point)) 1170 (let ((inhibit-read-only t) 1171 (yank-undo-function #'(lambda (_start _end) (vterm-undo)))) 1172 (cl-letf (((symbol-function 'insert-for-yank) #'vterm-insert)) 1173 (yank-pop arg)))) 1174 1175 (defun vterm-mouse-set-point (event &optional promote-to-region) 1176 "Move point to the position clicked on with the mouse. 1177 But when clicking to the unused area below the last prompt, 1178 move the cursor to the prompt area." 1179 (interactive "e\np") 1180 (let ((pt (mouse-set-point event promote-to-region))) 1181 (if (= (count-words pt (point-max)) 0) 1182 (vterm-reset-cursor-point) 1183 pt)) 1184 ;; Otherwise it selects text for every other click 1185 (keyboard-quit)) 1186 1187 (defun vterm-send-string (string &optional paste-p) 1188 "Send the string STRING to vterm. 1189 Optional argument PASTE-P paste-p." 1190 (when vterm--term 1191 (when paste-p 1192 (vterm--update vterm--term "<start_paste>" )) 1193 (dolist (char (string-to-list string)) 1194 (vterm--update vterm--term (char-to-string char))) 1195 (when paste-p 1196 (vterm--update vterm--term "<end_paste>"))) 1197 (setq vterm--redraw-immididately t) 1198 (accept-process-output vterm--process vterm-timer-delay nil t)) 1199 1200 (defun vterm-insert (&rest contents) 1201 "Insert the arguments, either strings or characters, at point. 1202 1203 Provide similar behavior as `insert' for vterm." 1204 (when vterm--term 1205 (vterm--update vterm--term "<start_paste>") 1206 (dolist (c contents) 1207 (if (characterp c) 1208 (vterm--update vterm--term (char-to-string c)) 1209 (dolist (char (string-to-list c)) 1210 (vterm--update vterm--term (char-to-string char))))) 1211 (vterm--update vterm--term "<end_paste>") 1212 (setq vterm--redraw-immididately t) 1213 (accept-process-output vterm--process vterm-timer-delay nil t))) 1214 1215 (defun vterm-delete-region (start end) 1216 "Delete the text between START and END for vterm. " 1217 (when vterm--term 1218 (save-excursion 1219 (when (get-text-property start 'vterm-line-wrap) 1220 ;; skip over the fake newline when start there. 1221 (setq start (1+ start)))) 1222 ;; count of chars after fake newline removed 1223 (let ((count (length (filter-buffer-substring start end)))) 1224 (if (vterm-goto-char start) 1225 (cl-loop repeat count do 1226 (vterm-send-key "<delete>" nil nil nil t)) 1227 (let ((inhibit-read-only nil)) 1228 (vterm--delete-region start end)))))) 1229 1230 (defun vterm-goto-char (pos) 1231 "Set point to POSITION for vterm. 1232 1233 The return value is `t' when point moved successfully." 1234 (when (and vterm--term 1235 (vterm-cursor-in-command-buffer-p) 1236 (vterm-cursor-in-command-buffer-p pos)) 1237 (vterm-reset-cursor-point) 1238 (let ((diff (- pos (point)))) 1239 (cond 1240 ((zerop diff) t) ;do not need move 1241 ((< diff 0) ;backward 1242 (while (and 1243 (vterm--backward-char) 1244 (> (point) pos))) 1245 (<= (point) pos)) 1246 (t 1247 (while (and (vterm--forward-char) 1248 (< (point) pos))) 1249 (>= (point) pos)))))) 1250 1251 ;;; Internal 1252 1253 (defun vterm--forward-char () 1254 "Move point 1 character forward (). 1255 1256 the return value is `t' when cursor moved." 1257 (vterm-reset-cursor-point) 1258 (let ((pt (point))) 1259 (vterm-send-key "<right>" nil nil nil t) 1260 (cond 1261 ((= (point) (1+ pt)) t) 1262 ((and (> (point) pt) 1263 ;; move over the fake newline 1264 (get-text-property (1- (point)) 'vterm-line-wrap)) 1265 t) 1266 ((and (= (point) (+ 4 pt)) 1267 (looking-back (regexp-quote "^[[C") nil)) ;escape code for <right> 1268 (dotimes (_ 3) (vterm-send-key "<backspace>" nil nil nil t)) ;;delete "^[[C" 1269 nil) 1270 ((> (point) (1+ pt)) ;auto suggest 1271 (vterm-send-key "_" nil nil t t) ;undo C-_ 1272 nil) 1273 (t nil)))) 1274 1275 1276 1277 (defun vterm--backward-char () 1278 "Move point N characters backward. 1279 1280 Return count of moved characeters." 1281 (vterm-reset-cursor-point) 1282 (let ((pt (point))) 1283 (vterm-send-key "<left>" nil nil nil t) 1284 (cond 1285 ((= (point) (1- pt)) t) 1286 ((and (= (point) (- pt 2)) 1287 ;; backward cross fake newline 1288 (string-equal (buffer-substring-no-properties 1289 (1+ (point)) (+ 2 (point))) 1290 "\n")) 1291 t) 1292 ((and (= (point) (+ 4 pt)) 1293 (looking-back (regexp-quote "^[[D") nil)) ;escape code for <left> 1294 (dotimes (_ 3) (vterm-send-key "<backspace>" nil nil nil t)) ;;delete "^[[D" 1295 nil) 1296 (t nil)))) 1297 1298 (defun vterm--delete-region(start end) 1299 "A wrapper for `delete-region'." 1300 (funcall vterm--delete-region-function start end)) 1301 1302 (defun vterm--insert(&rest content) 1303 "A wrapper for `insert'." 1304 (apply vterm--insert-function content)) 1305 1306 (defun vterm--delete-char(n &optional killflag) 1307 "A wrapper for `delete-char'." 1308 (funcall vterm--delete-char-function n killflag)) 1309 1310 (defun vterm--translate-event-to-args (event &optional meta) 1311 "Translate EVENT as list of args for `vterm-send-key'. 1312 1313 When some input method is enabled, one key may generate 1314 several characters, so the result of this function is a list, 1315 looks like: ((\"m\" :shift ))" 1316 (let* ((modifiers (event-modifiers event)) 1317 (shift (memq 'shift modifiers)) 1318 (meta (or meta (memq 'meta modifiers))) 1319 (ctrl (memq 'control modifiers)) 1320 (raw-key (event-basic-type event)) 1321 (ev-keys) keys) 1322 (if input-method-function 1323 (let ((inhibit-read-only t)) 1324 (setq ev-keys (funcall input-method-function raw-key)) 1325 (when (listp ev-keys) 1326 (dolist (k ev-keys) 1327 (when-let ((key (key-description (vector k)))) 1328 (when (and (not (symbolp event)) shift (not meta) (not ctrl)) 1329 (setq key (upcase key))) 1330 (setq keys (append keys (list (list key shift meta ctrl)))))))) 1331 (when-let ((key (key-description (vector raw-key)))) 1332 (when (and (not (symbolp event)) shift (not meta) (not ctrl)) 1333 (setq key (upcase key))) 1334 (setq keys (list (list key shift meta ctrl))))) 1335 keys)) 1336 1337 (defun vterm--invalidate () 1338 "The terminal buffer is invalidated, the buffer needs redrawing." 1339 (if (and (not vterm--redraw-immididately) 1340 vterm-timer-delay) 1341 (unless vterm--redraw-timer 1342 (setq vterm--redraw-timer 1343 (run-with-timer vterm-timer-delay nil 1344 #'vterm--delayed-redraw (current-buffer)))) 1345 (vterm--delayed-redraw (current-buffer)) 1346 (setq vterm--redraw-immididately nil))) 1347 1348 (defun vterm-check-proc (&optional buffer) 1349 "Check if there is a running process associated to the vterm buffer BUFFER. 1350 1351 BUFFER can be either a buffer or the name of one." 1352 (let* ((buffer (get-buffer (or buffer (current-buffer)))) 1353 (proc (get-buffer-process buffer))) 1354 (and proc 1355 (memq (process-status proc) '(run stop open listen connect)) 1356 (buffer-local-value 'vterm--term buffer)))) 1357 1358 (defun vterm--delayed-redraw (buffer) 1359 "Redraw the terminal buffer. 1360 Argument BUFFER the terminal buffer." 1361 (when (buffer-live-p buffer) 1362 (with-current-buffer buffer 1363 (let ((inhibit-redisplay t) 1364 (inhibit-read-only t) 1365 (windows (get-buffer-window-list))) 1366 (setq vterm--redraw-timer nil) 1367 (when vterm--term 1368 (vterm--redraw vterm--term) 1369 (unless (zerop (window-hscroll)) 1370 (when (cl-member (selected-window) windows :test #'eq) 1371 (set-window-hscroll (selected-window) 0)))))))) 1372 1373 ;; see VTermSelectionMask in vterm.el 1374 ;; VTERM_SELECTION_CLIPBOARD = (1<<0), 1375 ;; VTERM_SELECTION_PRIMARY = (1<<1), 1376 (defconst vterm--selection-clipboard 1) ;(1<<0) 1377 (defconst vterm--selection-primary 2) ;(1<<1) 1378 (defun vterm--set-selection (mask data) 1379 "OSC 52 Manipulate Selection Data. 1380 Search Manipulate Selection Data in 1381 https://invisible-island.net/xterm/ctlseqs/ctlseqs.html ." 1382 (when vterm-enable-manipulate-selection-data-by-osc52 1383 (let ((select-enable-clipboard select-enable-clipboard) 1384 (select-enable-primary select-enable-primary)) 1385 (setq select-enable-clipboard 1386 (logand mask vterm--selection-clipboard)) 1387 (setq select-enable-primary 1388 (logand mask vterm--selection-primary)) 1389 (kill-new data) 1390 (message "kill-ring is updated by vterm OSC 52(Manipulate Selection Data)")) 1391 )) 1392 1393 ;;; Entry Points 1394 1395 ;;;###autoload 1396 (defun vterm (&optional arg) 1397 "Create an interactive Vterm buffer. 1398 Start a new Vterm session, or switch to an already active 1399 session. Return the buffer selected (or created). 1400 1401 With a nonnumeric prefix arg, create a new session. 1402 1403 With a string prefix arg, create a new session with arg as buffer name. 1404 1405 With a numeric prefix arg (as in `C-u 42 M-x vterm RET'), switch 1406 to the session with that number, or create it if it doesn't 1407 already exist. 1408 1409 The buffer name used for Vterm sessions is determined by the 1410 value of `vterm-buffer-name'." 1411 (interactive "P") 1412 (vterm--internal #'pop-to-buffer-same-window arg)) 1413 1414 ;;;###autoload 1415 (defun vterm-other-window (&optional arg) 1416 "Create an interactive Vterm buffer in another window. 1417 Start a new Vterm session, or switch to an already active 1418 session. Return the buffer selected (or created). 1419 1420 With a nonnumeric prefix arg, create a new session. 1421 1422 With a string prefix arg, create a new session with arg as buffer name. 1423 1424 With a numeric prefix arg (as in `C-u 42 M-x vterm RET'), switch 1425 to the session with that number, or create it if it doesn't 1426 already exist. 1427 1428 The buffer name used for Vterm sessions is determined by the 1429 value of `vterm-buffer-name'." 1430 (interactive "P") 1431 (vterm--internal #'pop-to-buffer arg)) 1432 1433 (defun vterm--internal (pop-to-buf-fun &optional arg) 1434 (cl-assert vterm-buffer-name) 1435 (let ((buf (cond ((numberp arg) 1436 (get-buffer-create (format "%s<%d>" 1437 vterm-buffer-name 1438 arg))) 1439 ((stringp arg) (generate-new-buffer arg)) 1440 (arg (generate-new-buffer vterm-buffer-name)) 1441 (t 1442 (get-buffer-create vterm-buffer-name))))) 1443 (cl-assert (and buf (buffer-live-p buf))) 1444 (funcall pop-to-buf-fun buf) 1445 (with-current-buffer buf 1446 (unless (derived-mode-p 'vterm-mode) 1447 (vterm-mode))) 1448 buf)) 1449 1450 ;;; Internal 1451 1452 (defun vterm--flush-output (output) 1453 "Send the virtual terminal's OUTPUT to the shell." 1454 (process-send-string vterm--process output)) 1455 ;; Terminal emulation 1456 ;; This is the standard process filter for term buffers. 1457 ;; It emulates (most of the features of) a VT100/ANSI-style terminal. 1458 1459 ;; References: 1460 ;; [ctlseqs]: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html 1461 ;; [ECMA-48]: https://www.ecma-international.org/publications/standards/Ecma-048.htm 1462 ;; [vt100]: https://vt100.net/docs/vt100-ug/chapter3.html 1463 1464 (defconst vterm-control-seq-regexp 1465 (concat 1466 ;; A control character, 1467 "\\(?:[\r\n\000\007\t\b\016\017]\\|" 1468 ;; a C1 escape coded character (see [ECMA-48] section 5.3 "Elements 1469 ;; of the C1 set"), 1470 "\e\\(?:[DM78c=]\\|" 1471 ;; another Emacs specific control sequence for term.el, 1472 "AnSiT[^\n]+\n\\|" 1473 ;; another Emacs specific control sequence for vterm.el 1474 ;; printf "\e]%s\e\\" 1475 "\\][^\e]+\e\\\\\\|" 1476 ;; or an escape sequence (section 5.4 "Control Sequences"), 1477 "\\[\\([\x30-\x3F]*\\)[\x20-\x2F]*[\x40-\x7E]\\)\\)") 1478 "Regexp matching control sequences handled by term.el.") 1479 1480 (defconst vterm-control-seq-prefix-regexp 1481 "[\032\e]") 1482 1483 (defun vterm--filter (process input) 1484 "I/O Event. Feeds PROCESS's INPUT to the virtual terminal. 1485 1486 Then triggers a redraw from the module." 1487 (let ((inhibit-redisplay t) 1488 (inhibit-eol-conversion t) 1489 (inhibit-read-only t) 1490 (buf (process-buffer process)) 1491 (i 0) 1492 (str-length (length input)) 1493 decoded-substring 1494 funny) 1495 (when (buffer-live-p buf) 1496 (with-current-buffer buf 1497 ;; borrowed from term.el 1498 ;; Handle non-control data. Decode the string before 1499 ;; counting characters, to avoid garbling of certain 1500 ;; multibyte characters (https://github.com/akermu/emacs-libvterm/issues/394). 1501 ;; same bug of term.el https://debbugs.gnu.org/cgi/bugreport.cgi?bug=1006 1502 (when vterm--undecoded-bytes 1503 (setq input (concat vterm--undecoded-bytes input)) 1504 (setq vterm--undecoded-bytes nil) 1505 (setq str-length (length input))) 1506 (while (< i str-length) 1507 (setq funny (string-match vterm-control-seq-regexp input i)) 1508 (let ((ctl-end (if funny (match-end 0) 1509 (setq funny (string-match vterm-control-seq-prefix-regexp input i)) 1510 (if funny 1511 (setq vterm--undecoded-bytes 1512 (substring input funny)) 1513 (setq funny str-length)) 1514 ;; The control sequence ends somewhere 1515 ;; past the end of this string. 1516 (1+ str-length)))) 1517 (when (> funny i) 1518 ;; Handle non-control data. Decode the string before 1519 ;; counting characters, to avoid garbling of certain 1520 ;; multibyte characters (emacs bug#1006). 1521 (setq decoded-substring 1522 (decode-coding-string 1523 (substring input i funny) 1524 locale-coding-system t)) 1525 ;; Check for multibyte characters that ends 1526 ;; before end of string, and save it for 1527 ;; next time. 1528 (when (= funny str-length) 1529 (let ((partial 0) 1530 (count (length decoded-substring))) 1531 (while (and (< partial count) 1532 (eq (char-charset (aref decoded-substring 1533 (- count 1 partial))) 1534 'eight-bit)) 1535 (cl-incf partial)) 1536 (when (> (1+ count) partial 0) 1537 (setq vterm--undecoded-bytes 1538 (substring decoded-substring (- partial))) 1539 (setq decoded-substring 1540 (substring decoded-substring 0 (- partial))) 1541 (cl-decf str-length partial) 1542 (cl-decf funny partial)))) 1543 (ignore-errors (vterm--write-input vterm--term decoded-substring)) 1544 (setq i funny)) 1545 (when (<= ctl-end str-length) 1546 (ignore-errors (vterm--write-input vterm--term (substring input i ctl-end)))) 1547 (setq i ctl-end))) 1548 (vterm--update vterm--term))))) 1549 1550 (defun vterm--sentinel (process event) 1551 "Sentinel of vterm PROCESS. 1552 Argument EVENT process event." 1553 (let ((buf (process-buffer process))) 1554 (run-hook-with-args 'vterm-exit-functions 1555 (if (buffer-live-p buf) buf nil) 1556 event) 1557 (if (and vterm-kill-buffer-on-exit (buffer-live-p buf)) 1558 (kill-buffer buf)))) 1559 1560 (defun vterm--text-scale-mode (&optional _argv) 1561 "Fix `line-number' height for scaled text." 1562 (and text-scale-mode 1563 (or (equal major-mode 'vterm-mode) 1564 (derived-mode-p 'vterm-mode)) 1565 (boundp 'display-line-numbers) 1566 (let ((height (expt text-scale-mode-step 1567 text-scale-mode-amount))) 1568 (when vterm--linenum-remapping 1569 (face-remap-remove-relative vterm--linenum-remapping)) 1570 (setq vterm--linenum-remapping 1571 (face-remap-add-relative 'line-number :height height)))) 1572 (window--adjust-process-windows)) 1573 1574 (advice-add #'text-scale-mode :after #'vterm--text-scale-mode) 1575 1576 (defun vterm--window-adjust-process-window-size (process windows) 1577 "Adjust width of window WINDOWS associated to process PROCESS. 1578 1579 `vterm-min-window-width' determines the minimum width allowed." 1580 ;; We want `vterm-copy-mode' to resemble a fundamental buffer as much as 1581 ;; possible. Hence, we must not call this function when the minor mode is 1582 ;; enabled, otherwise the buffer would be redrawn, messing around with the 1583 ;; position of the point. 1584 (unless vterm-copy-mode 1585 (let* ((size (funcall window-adjust-process-window-size-function 1586 process windows)) 1587 (width (car size)) 1588 (height (cdr size)) 1589 (inhibit-read-only t)) 1590 (setq width (- width (vterm--get-margin-width))) 1591 (setq width (max width vterm-min-window-width)) 1592 (when (and (processp process) 1593 (process-live-p process) 1594 (> width 0) 1595 (> height 0)) 1596 (vterm--set-size vterm--term height width) 1597 (cons width height))))) 1598 1599 (defun vterm--get-margin-width () 1600 "Get margin width of vterm buffer when `display-line-numbers-mode' is enabled." 1601 (let ((width 0) 1602 (max-line-num (+ (frame-height) vterm-max-scrollback))) 1603 (when (bound-and-true-p display-line-numbers) 1604 (setq width (+ width 4 1605 (string-width (number-to-string max-line-num))))) 1606 width)) 1607 1608 (defun vterm--delete-lines (line-num count &optional delete-whole-line) 1609 "Delete COUNT lines from LINE-NUM. 1610 If LINE-NUM is negative backward-line from end of buffer. 1611 If option DELETE-WHOLE-LINE is non-nil, then this command kills 1612 the whole line including its terminating newline" 1613 (save-excursion 1614 (when (vterm--goto-line line-num) 1615 (vterm--delete-region (point) (line-end-position count)) 1616 (when (and delete-whole-line 1617 (looking-at "\n")) 1618 (vterm--delete-char 1))))) 1619 1620 (defun vterm--goto-line (n) 1621 "Go to line N and return true on success. 1622 If N is negative backward-line from end of buffer." 1623 (cond 1624 ((> n 0) 1625 (goto-char (point-min)) 1626 (eq 0 (forward-line (1- n)))) 1627 (t 1628 (goto-char (point-max)) 1629 (eq 0 (forward-line n))))) 1630 1631 (defun vterm--set-title (title) 1632 "Use TITLE to set the buffer name according to `vterm-buffer-name-string'." 1633 (when vterm-buffer-name-string 1634 (rename-buffer (format vterm-buffer-name-string title) t))) 1635 1636 (defun vterm--set-directory (path) 1637 "Set `default-directory' to PATH." 1638 (let ((dir (vterm--get-directory path))) 1639 (when dir (setq default-directory dir)))) 1640 1641 (defun vterm--get-directory (path) 1642 "Get normalized directory to PATH." 1643 (when path 1644 (let (directory) 1645 (if (string-match "^\\(.*?\\)@\\(.*?\\):\\(.*?\\)$" path) 1646 (progn 1647 (let ((user (match-string 1 path)) 1648 (host (match-string 2 path)) 1649 (dir (match-string 3 path))) 1650 (if (and (string-equal user user-login-name) 1651 (string-equal host (system-name))) 1652 (progn 1653 (when (file-directory-p dir) 1654 (setq directory (file-name-as-directory dir)))) 1655 (setq directory (file-name-as-directory (concat "/-:" path)))))) 1656 (when (file-directory-p path) 1657 (setq directory (file-name-as-directory path)))) 1658 directory))) 1659 1660 (defun vterm--get-pwd (&optional linenum) 1661 "Get working directory at LINENUM." 1662 (when vterm--term 1663 (let ((raw-pwd (vterm--get-pwd-raw 1664 vterm--term 1665 (or linenum (line-number-at-pos))))) 1666 (when raw-pwd 1667 (vterm--get-directory raw-pwd))))) 1668 1669 (defun vterm--get-color (index &rest args) 1670 "Get color by INDEX from `vterm-color-palette'. 1671 1672 Special INDEX of -1 is used to represent default colors. ARGS 1673 may optionally contain `:underline' or `:inverse-video' for cells 1674 with underline or inverse video attribute. If ARGS contains 1675 `:foreground', use foreground color of the respective face 1676 instead of background." 1677 (let ((foreground (member :foreground args)) 1678 (underline (member :underline args)) 1679 (inverse-video (member :inverse-video args))) 1680 (funcall (if foreground #'face-foreground #'face-background) 1681 (cond 1682 ((and (>= index 0) (< index 16)) 1683 (elt vterm-color-palette index)) 1684 ((and (= index -1) foreground underline) 1685 'vterm-color-underline) 1686 ((and (= index -1) (not foreground) inverse-video) 1687 'vterm-color-inverse-video) 1688 (t 'default)) 1689 nil 'default))) 1690 1691 (defun vterm--eval (str) 1692 "Check if string STR is `vterm-eval-cmds' and execute command. 1693 1694 All passed in arguments are strings and forwarded as string to 1695 the called functions." 1696 (let* ((parts (split-string-and-unquote str)) 1697 (command (car parts)) 1698 (args (cdr parts)) 1699 (f (assoc command vterm-eval-cmds))) 1700 (if f 1701 (apply (cadr f) args) 1702 (message "Failed to find command: %s. To execute a command, 1703 add it to the `vterm-eval-cmd' list" command)))) 1704 1705 ;; TODO: Improve doc string, it should not point to the readme but it should 1706 ;; be self-contained. 1707 (defun vterm--prompt-tracking-enabled-p () 1708 "Return t if tracking the prompt is enabled. 1709 1710 Prompt tracking need shell side configurations. 1711 1712 For zsh user, this is done by PROMPT=$PROMPT'%{$(vterm_prompt_end)%}'. 1713 1714 The shell send semantic information about where the prompt ends via properly 1715 escaped sequences to Emacs. 1716 1717 More information see `Shell-side configuration' and `Directory tracking' 1718 in README." 1719 (or vterm--prompt-tracking-enabled-p 1720 (save-excursion 1721 (setq vterm--prompt-tracking-enabled-p 1722 (next-single-property-change (point-min) 'vterm-prompt))))) 1723 1724 (defun vterm-next-prompt (n) 1725 "Move to end of Nth next prompt in the buffer." 1726 (interactive "p") 1727 (if (and vterm-use-vterm-prompt-detection-method 1728 (vterm--prompt-tracking-enabled-p)) 1729 (let ((pt (point)) 1730 (promp-pt (vterm--get-prompt-point))) 1731 (when promp-pt (goto-char promp-pt)) 1732 (cl-loop repeat (or n 1) do 1733 (setq pt (next-single-property-change (line-beginning-position 2) 'vterm-prompt)) 1734 (when pt (goto-char pt)))) 1735 (term-next-prompt n))) 1736 1737 (defun vterm-previous-prompt (n) 1738 "Move to end of Nth previous prompt in the buffer." 1739 (interactive "p") 1740 (if (and vterm-use-vterm-prompt-detection-method 1741 (vterm--prompt-tracking-enabled-p)) 1742 (let ((pt (point)) 1743 (prompt-pt (vterm--get-prompt-point))) 1744 (when prompt-pt 1745 (goto-char prompt-pt) 1746 (when (> pt (point)) 1747 (setq n (1- (or n 1)))) 1748 (cl-loop repeat n do 1749 (setq pt (previous-single-property-change (1- (point)) 'vterm-prompt)) 1750 (when pt (goto-char (1- pt)))))) 1751 (term-previous-prompt n))) 1752 1753 (defun vterm--get-beginning-of-line (&optional pt) 1754 "Find the start of the line, bypassing line wraps. 1755 If PT is specified, find it's beginning of the line instead of the beginning 1756 of the line at cursor." 1757 (save-excursion 1758 (when pt (goto-char pt)) 1759 (beginning-of-line) 1760 (while (and (not (bobp)) 1761 (get-text-property (1- (point)) 'vterm-line-wrap)) 1762 (forward-char -1) 1763 (beginning-of-line)) 1764 (point))) 1765 1766 (defun vterm--get-end-of-line (&optional pt) 1767 "Find the start of the line, bypassing line wraps. 1768 If PT is specified, find it's end of the line instead of the end 1769 of the line at cursor." 1770 (save-excursion 1771 (when pt (goto-char pt)) 1772 (end-of-line) 1773 (while (get-text-property (point) 'vterm-line-wrap) 1774 (forward-char) 1775 (end-of-line)) 1776 (point))) 1777 1778 ;; TODO: Improve doc string, it should not point to the readme but it should 1779 ;; be self-contained. 1780 (defun vterm--get-prompt-point () 1781 "Get the position of the end of current prompt. 1782 More information see `vterm--prompt-tracking-enabled-p' and 1783 `Directory tracking and Prompt tracking'in README." 1784 (let ((end-point (vterm--get-end-of-line)) 1785 prompt-point) 1786 (save-excursion 1787 (if (and vterm-use-vterm-prompt-detection-method 1788 (vterm--prompt-tracking-enabled-p)) 1789 (if (get-text-property end-point 'vterm-prompt) 1790 end-point 1791 (setq prompt-point (previous-single-property-change end-point 'vterm-prompt)) 1792 (when prompt-point (setq prompt-point (1- prompt-point)))) 1793 (goto-char end-point) 1794 (if (search-backward-regexp term-prompt-regexp nil t) 1795 (goto-char (match-end 0)) 1796 (vterm--get-beginning-of-line)))))) 1797 1798 (defun vterm--at-prompt-p () 1799 "Return t if the cursor position is at shell prompt." 1800 (= (point) (or (vterm--get-prompt-point) 0))) 1801 1802 (defun vterm-cursor-in-command-buffer-p (&optional pt) 1803 "Check whether cursor in command buffer area." 1804 (save-excursion 1805 (vterm-reset-cursor-point) 1806 (let ((promp-pt (vterm--get-prompt-point))) 1807 (when promp-pt 1808 (<= promp-pt (or pt (vterm--get-cursor-point))))))) 1809 1810 (defun vterm-beginning-of-line () 1811 "Move point to the beginning of the line. 1812 1813 Move the point to the first character after the shell prompt on this line. 1814 If the point is already there, move to the beginning of the line. 1815 Effectively toggle between the two positions." 1816 (interactive "^") 1817 (if (vterm--at-prompt-p) 1818 (goto-char (vterm--get-beginning-of-line)) 1819 (goto-char (max (or (vterm--get-prompt-point) 0) 1820 (vterm--get-beginning-of-line))))) 1821 1822 (defun vterm-end-of-line () 1823 "Move point to the end of the line, bypassing line wraps." 1824 (interactive "^") 1825 (goto-char (vterm--get-end-of-line))) 1826 1827 (defun vterm-reset-cursor-point () 1828 "Make sure the cursor at the right position." 1829 (interactive) 1830 (when vterm--term 1831 (let ((inhibit-read-only t)) 1832 (vterm--reset-point vterm--term)))) 1833 1834 (defun vterm--get-cursor-point () 1835 "Get term cursor position." 1836 (when vterm--term 1837 (save-excursion 1838 (vterm-reset-cursor-point)))) 1839 1840 (defun vterm--reinsert-fake-newlines () 1841 "Reinsert fake newline from `vterm--copy-mode-fake-newlines'." 1842 (let ((inhibit-read-only t) 1843 (inhibit-redisplay t) 1844 (fake-newline-text "\n") 1845 fake-newline-pos) 1846 (add-text-properties 0 1 '(vterm-line-wrap t rear-nonsticky t) 1847 fake-newline-text) 1848 (while vterm--copy-mode-fake-newlines 1849 (setq fake-newline-pos (car vterm--copy-mode-fake-newlines)) 1850 (setq vterm--copy-mode-fake-newlines (cdr vterm--copy-mode-fake-newlines)) 1851 (goto-char fake-newline-pos) 1852 (insert fake-newline-text)))) 1853 1854 (defun vterm--remove-fake-newlines (&optional remembering-pos-p) 1855 "Filter out injected newlines were injected when rendering the terminal. 1856 1857 These newlines were tagged with \\='vterm-line-wrap property so we 1858 can find them and remove them. 1859 If REMEMBERING-POS-P is not nil remembering their positions in a buffer-local 1860 `vterm--copy-mode-fake-newlines'." 1861 (let (fake-newline 1862 (inhibit-read-only t) 1863 (inhibit-redisplay t)) 1864 (when remembering-pos-p 1865 (setq vterm--copy-mode-fake-newlines nil)) 1866 1867 (goto-char (point-max)) 1868 (when (and (bolp) 1869 (not (bobp)) 1870 (get-text-property (1- (point)) 'vterm-line-wrap)) 1871 (forward-char -1) 1872 (when remembering-pos-p 1873 (setq vterm--copy-mode-fake-newlines 1874 (cons (point) vterm--copy-mode-fake-newlines))) 1875 (vterm--delete-char 1)) 1876 1877 (while (and (not (bobp)) 1878 (setq fake-newline (previous-single-property-change 1879 (point) 'vterm-line-wrap))) 1880 (goto-char (1- fake-newline)) 1881 (cl-assert (eq ?\n (char-after))) 1882 (when remembering-pos-p 1883 (setq vterm--copy-mode-fake-newlines 1884 (cons (point) vterm--copy-mode-fake-newlines))) 1885 (vterm--delete-char 1)))) 1886 1887 (defun vterm--filter-buffer-substring (content) 1888 "Filter string CONTENT of fake/injected newlines." 1889 (with-temp-buffer 1890 (vterm--insert content) 1891 (vterm--remove-fake-newlines nil) 1892 (buffer-string))) 1893 1894 1895 (provide 'vterm) 1896 ;; Local Variables: 1897 ;; indent-tabs-mode: nil 1898 ;; End: 1899 ;;; vterm.el ends here