config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

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