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