config

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

vterm.el (70209B)


      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 (or (eq major-mode 'vterm-mode)
    876             (derived-mode-p 'vterm-mode))
    877     (setq next-error-function 'vterm-next-error-function)))
    878 
    879 (add-hook 'compilation-shell-minor-mode-hook #'vterm--compilation-setup)
    880 
    881 ;;;###autoload
    882 (defun vterm-next-error-function (n &optional reset)
    883   "Advance to the next error message and visit the file where the error was.
    884 This is the value of `next-error-function' in Compilation
    885 buffers.  Prefix arg N says how many error messages to move
    886 forwards (or backwards, if negative).
    887 
    888 Optional argument RESET clears all the errors."
    889   (interactive "p")
    890   (let* ((pt (point))
    891          (default-directory default-directory)
    892          (pwd (vterm--get-pwd)))
    893     (when pwd
    894       (setq default-directory pwd))
    895     (goto-char pt)
    896     (compilation-next-error-function n reset)))
    897 
    898 ;;; Copy Mode
    899 
    900 (defun vterm--enter-copy-mode ()
    901   (use-local-map nil)
    902   (vterm-send-stop)
    903   (when vterm-copy-mode-remove-fake-newlines
    904     (save-excursion
    905       (setq truncate-lines nil)
    906       (vterm--remove-fake-newlines t))))
    907 
    908 
    909 (defun vterm--exit-copy-mode ()
    910   (when vterm-copy-mode-remove-fake-newlines
    911     (save-excursion
    912       (setq truncate-lines t)
    913       (vterm--reinsert-fake-newlines)))
    914   (vterm-reset-cursor-point)
    915   (use-local-map vterm-mode-map)
    916   (vterm-send-start))
    917 
    918 (define-minor-mode vterm-copy-mode
    919   "Toggle `vterm-copy-mode'.
    920 
    921 When `vterm-copy-mode' is enabled, the terminal will not display
    922 additional output received from the underlying process and will
    923 behave similarly to buffer in `fundamental-mode'.  This mode is
    924 typically used to copy text from vterm buffers.
    925 
    926 A conventient way to exit `vterm-copy-mode' is with
    927 `vterm-copy-mode-done', which copies the selected text and exit
    928 `vterm-copy-mode'."
    929   :group 'vterm
    930   :lighter " VTermCopy"
    931   :keymap vterm-copy-mode-map
    932   (if (or (equal major-mode 'vterm-mode)
    933           (derived-mode-p 'vterm-mode))
    934       (if vterm-copy-mode
    935           (vterm--enter-copy-mode)
    936         (vterm--exit-copy-mode))
    937     (user-error "You cannot enable vterm-copy-mode outside vterm buffers")))
    938 
    939 (defun vterm-copy-mode-done (arg)
    940   "Save the active region or line to the kill ring and exit `vterm-copy-mode'.
    941 
    942 If a region is defined then that region is killed, with no region then
    943 current line is killed from start to end.
    944 
    945 The option `vterm-copy-exclude-prompt' controls if the prompt
    946 should be included in a line copy.  Using the universal prefix ARG
    947 will invert `vterm-copy-exclude-prompt' for that call."
    948   (interactive "P")
    949   (unless vterm-copy-mode
    950     (user-error "This command is effective only in vterm-copy-mode"))
    951   (unless (use-region-p)
    952     (goto-char (vterm--get-beginning-of-line))
    953     ;; Are we excluding the prompt?
    954     (if (or (and vterm-copy-exclude-prompt (not arg))
    955             (and (not vterm-copy-exclude-prompt) arg))
    956         (goto-char (max (or (vterm--get-prompt-point) 0)
    957                         (vterm--get-beginning-of-line))))
    958     (set-mark (point))
    959     (goto-char (vterm--get-end-of-line)))
    960   (kill-ring-save (region-beginning) (region-end))
    961   (vterm-copy-mode -1))
    962 
    963 ;;; Commands
    964 
    965 (defun vterm--self-insert-meta ()
    966   (interactive)
    967   (when vterm--term
    968     (dolist (key (vterm--translate-event-to-args
    969                   last-command-event :meta))
    970       (apply #'vterm-send-key key))))
    971 
    972 (defun vterm--self-insert ()
    973   "Send invoking key to libvterm."
    974   (interactive)
    975   (when vterm--term
    976     (dolist (key (vterm--translate-event-to-args
    977                   last-command-event))
    978       (apply #'vterm-send-key key))))
    979 
    980 (defun vterm-send-key (key &optional shift meta ctrl accept-proc-output)
    981   "Send KEY to libvterm with optional modifiers SHIFT, META and CTRL."
    982   (deactivate-mark)
    983   (when vterm--term
    984     (let ((inhibit-redisplay t)
    985           (inhibit-read-only t))
    986       (vterm--update vterm--term key shift meta ctrl)
    987       (setq vterm--redraw-immididately t)
    988       (when accept-proc-output
    989         (accept-process-output vterm--process vterm-timer-delay nil t)))))
    990 
    991 (defun vterm-send (key)
    992   "Send KEY to libvterm.  KEY can be anything `kbd' understands."
    993   (dolist (key (vterm--translate-event-to-args
    994                 (listify-key-sequence (kbd key))))
    995     (apply #'vterm-send-key key)))
    996 
    997 (defun vterm-send-next-key ()
    998   "Read next input event and send it to the libvterm.
    999 
   1000 With this you can directly send modified keys to applications
   1001 running in the terminal (like Emacs or Nano)."
   1002   (interactive)
   1003   (dolist (key (vterm--translate-event-to-args
   1004                 (read-event)))
   1005     (apply #'vterm-send-key key)))
   1006 
   1007 (defun vterm-send-start ()
   1008   "Output from the system is started when the system receives START."
   1009   (interactive)
   1010   (vterm-send-key "<start>"))
   1011 
   1012 (defun vterm-send-stop ()
   1013   "Output from the system is stopped when the system receives STOP."
   1014   (interactive)
   1015   (vterm-send-key "<stop>"))
   1016 
   1017 (defun vterm-send-return ()
   1018   "Send `C-m' to the libvterm."
   1019   (interactive)
   1020   (deactivate-mark)
   1021   (when vterm--term
   1022     (if (vterm--get-icrnl vterm--term)
   1023         (process-send-string vterm--process "\C-j")
   1024       (process-send-string vterm--process "\C-m"))))
   1025 
   1026 (defun vterm-send-tab ()
   1027   "Send `<tab>' to the libvterm."
   1028   (interactive)
   1029   (vterm-send-key "<tab>"))
   1030 
   1031 (defun vterm-send-space ()
   1032   "Send `<space>' to the libvterm."
   1033   (interactive)
   1034   (vterm-send-key " "))
   1035 
   1036 (defun vterm-send-backspace ()
   1037   "Send `<backspace>' to the libvterm."
   1038   (interactive)
   1039   (vterm-send-key "<backspace>"))
   1040 
   1041 (defun vterm-send-delete ()
   1042   "Send `<delete>' to the libvterm."
   1043   (interactive)
   1044   (vterm-send-key "<delete>"))
   1045 
   1046 (defun vterm-send-meta-backspace ()
   1047   "Send `M-<backspace>' to the libvterm."
   1048   (interactive)
   1049   (vterm-send-key "<backspace>" nil t))
   1050 
   1051 (defun vterm-send-up ()
   1052   "Send `<up>' to the libvterm."
   1053   (interactive)
   1054   (vterm-send-key "<up>"))
   1055 (make-obsolete 'vterm-send-up 'vterm--self-insert "v0.1")
   1056 
   1057 (defun vterm-send-down ()
   1058   "Send `<down>' to the libvterm."
   1059   (interactive)
   1060   (vterm-send-key "<down>"))
   1061 (make-obsolete 'vterm-send-down 'vterm--self-insert "v0.1")
   1062 
   1063 (defun vterm-send-left ()
   1064   "Send `<left>' to the libvterm."
   1065   (interactive)
   1066   (vterm-send-key "<left>"))
   1067 (make-obsolete 'vterm-send-left 'vterm--self-insert "v0.1")
   1068 
   1069 (defun vterm-send-right ()
   1070   "Send `<right>' to the libvterm."
   1071   (interactive)
   1072   (vterm-send-key "<right>"))
   1073 (make-obsolete 'vterm-send-right 'vterm--self-insert "v0.1")
   1074 
   1075 (defun vterm-send-prior ()
   1076   "Send `<prior>' to the libvterm."
   1077   (interactive)
   1078   (vterm-send-key "<prior>"))
   1079 (make-obsolete 'vterm-send-prior 'vterm--self-insert "v0.1")
   1080 
   1081 (defun vterm-send-next ()
   1082   "Send `<next>' to the libvterm."
   1083   (interactive)
   1084   (vterm-send-key "<next>"))
   1085 (make-obsolete 'vterm-send-next 'vterm--self-insert "v0.1")
   1086 
   1087 (defun vterm-send-meta-dot ()
   1088   "Send `M-.' to the libvterm."
   1089   (interactive)
   1090   (vterm-send-key "." nil t))
   1091 (make-obsolete 'vterm-send-meta-dot 'vterm--self-insert "v0.1")
   1092 
   1093 (defun vterm-send-meta-comma ()
   1094   "Send `M-,' to the libvterm."
   1095   (interactive)
   1096   (vterm-send-key "," nil t))
   1097 (make-obsolete 'vterm-send-meta-comma 'vterm--self-insert "v0.1")
   1098 
   1099 (defun vterm-send-ctrl-slash ()
   1100   "Send `C-\' to the libvterm."
   1101   (interactive)
   1102   (vterm-send-key "\\" nil nil t))
   1103 (make-obsolete 'vterm-send-ctrl-slash 'vterm--self-insert "v0.1")
   1104 
   1105 (defun vterm-send-escape ()
   1106   "Send `<escape>' to the libvterm."
   1107   (interactive)
   1108   (vterm-send-key "<escape>"))
   1109 
   1110 (defun vterm-clear-scrollback ()
   1111   "Send `<clear-scrollback>' to the libvterm."
   1112   (interactive)
   1113   (vterm-send-key "<clear_scrollback>"))
   1114 
   1115 (defun vterm-clear (&optional arg)
   1116   "Send `<clear>' to the libvterm.
   1117 
   1118 `vterm-clear-scrollback' determines whether
   1119 `vterm-clear' should also clear the scrollback or not.
   1120 
   1121 This behavior can be altered by calling `vterm-clear' with a
   1122 prefix argument ARG or with \\[universal-argument]."
   1123   (interactive "P")
   1124   (if (or
   1125        (and vterm-clear-scrollback-when-clearing (not arg))
   1126        (and arg (not vterm-clear-scrollback-when-clearing)))
   1127       (vterm-clear-scrollback))
   1128   (vterm-send-key "l" nil nil :ctrl))
   1129 
   1130 (defun vterm-undo ()
   1131   "Send `C-_' to the libvterm."
   1132   (interactive)
   1133   (vterm-send-key "_" nil nil t))
   1134 
   1135 (defun vterm-yank (&optional arg)
   1136   "Yank (paste) text in vterm.
   1137 
   1138 Argument ARG is passed to `yank'."
   1139   (interactive "P")
   1140   (deactivate-mark)
   1141   (vterm-goto-char (point))
   1142   (let ((inhibit-read-only t))
   1143     (cl-letf (((symbol-function 'insert-for-yank) #'vterm-insert))
   1144       (yank arg))))
   1145 
   1146 (defun vterm-yank-primary ()
   1147   "Yank text from the primary selection in vterm."
   1148   (interactive)
   1149   (vterm-goto-char (point))
   1150   (let ((inhibit-read-only t)
   1151         (primary (gui-get-primary-selection)))
   1152     (cl-letf (((symbol-function 'insert-for-yank) #'vterm-insert))
   1153       (insert-for-yank primary))))
   1154 
   1155 (defun vterm-yank-pop (&optional arg)
   1156   "Replaced text just yanked with the next entry in the kill ring.
   1157 
   1158 Argument ARG is passed to `yank'"
   1159   (interactive "p")
   1160   (vterm-goto-char (point))
   1161   (let ((inhibit-read-only t)
   1162         (yank-undo-function #'(lambda (_start _end) (vterm-undo))))
   1163     (cl-letf (((symbol-function 'insert-for-yank) #'vterm-insert))
   1164       (yank-pop arg))))
   1165 
   1166 (defun vterm-mouse-set-point (event &optional promote-to-region)
   1167   "Move point to the position clicked on with the mouse.
   1168 But when clicking to the unused area below the last prompt,
   1169 move the cursor to the prompt area."
   1170   (interactive "e\np")
   1171   (let ((pt (mouse-set-point event promote-to-region)))
   1172     (if (= (count-words pt (point-max)) 0)
   1173         (vterm-reset-cursor-point)
   1174       pt))
   1175   ;; Otherwise it selects text for every other click
   1176   (keyboard-quit))
   1177 
   1178 (defun vterm-send-string (string &optional paste-p)
   1179   "Send the string STRING to vterm.
   1180 Optional argument PASTE-P paste-p."
   1181   (when vterm--term
   1182     (when paste-p
   1183       (vterm--update vterm--term "<start_paste>" ))
   1184     (dolist (char (string-to-list string))
   1185       (vterm--update vterm--term (char-to-string char)))
   1186     (when paste-p
   1187       (vterm--update vterm--term "<end_paste>")))
   1188   (setq vterm--redraw-immididately t)
   1189   (accept-process-output vterm--process vterm-timer-delay nil t))
   1190 
   1191 (defun vterm-insert (&rest contents)
   1192   "Insert the arguments, either strings or characters, at point.
   1193 
   1194 Provide similar behavior as `insert' for vterm."
   1195   (when vterm--term
   1196     (vterm--update vterm--term "<start_paste>")
   1197     (dolist (c contents)
   1198       (if (characterp c)
   1199           (vterm--update vterm--term (char-to-string c))
   1200         (dolist (char (string-to-list c))
   1201           (vterm--update vterm--term (char-to-string char)))))
   1202     (vterm--update vterm--term "<end_paste>")
   1203     (setq vterm--redraw-immididately t)
   1204     (accept-process-output vterm--process vterm-timer-delay nil t)))
   1205 
   1206 (defun vterm-delete-region (start end)
   1207   "Delete the text between START and END for vterm. "
   1208   (when vterm--term
   1209     (save-excursion
   1210       (when (get-text-property start 'vterm-line-wrap)
   1211         ;; skip over the fake newline when start there.
   1212         (setq start (1+ start))))
   1213     ;; count of chars after fake newline removed
   1214     (let ((count (length (filter-buffer-substring start end))))
   1215       (if (vterm-goto-char start)
   1216           (cl-loop repeat count do
   1217                    (vterm-send-key "<delete>" nil nil nil t))
   1218         (let ((inhibit-read-only nil))
   1219           (vterm--delete-region start end))))))
   1220 
   1221 (defun vterm-goto-char (pos)
   1222   "Set point to POSITION for vterm.
   1223 
   1224 The return value is `t' when point moved successfully."
   1225   (when (and vterm--term
   1226              (vterm-cursor-in-command-buffer-p)
   1227              (vterm-cursor-in-command-buffer-p pos))
   1228     (vterm-reset-cursor-point)
   1229     (let ((diff (- pos (point))))
   1230       (cond
   1231        ((zerop diff) t)                   ;do not need move
   1232        ((< diff 0)                        ;backward
   1233         (while (and
   1234                 (vterm--backward-char)
   1235                 (> (point) pos)))
   1236         (<= (point) pos))
   1237        (t
   1238         (while (and (vterm--forward-char)
   1239                     (< (point) pos)))
   1240         (>= (point) pos))))))
   1241 
   1242 ;;; Internal
   1243 
   1244 (defun vterm--forward-char ()
   1245   "Move point 1 character forward ().
   1246 
   1247 the return value is `t' when cursor moved."
   1248   (vterm-reset-cursor-point)
   1249   (let ((pt (point)))
   1250     (vterm-send-key "<right>" nil nil nil t)
   1251     (cond
   1252      ((= (point) (1+ pt)) t)
   1253      ((and (> (point) pt)
   1254            ;; move over the fake newline
   1255            (get-text-property (1- (point)) 'vterm-line-wrap))
   1256       t)
   1257      ((and (= (point) (+ 4 pt))
   1258            (looking-back (regexp-quote "^[[C") nil)) ;escape code for <right>
   1259       (dotimes (_ 3) (vterm-send-key "<backspace>" nil nil nil t)) ;;delete  "^[[C"
   1260       nil)
   1261      ((> (point) (1+ pt))             ;auto suggest
   1262       (vterm-send-key "_" nil nil t t) ;undo C-_
   1263       nil)
   1264      (t nil))))
   1265 
   1266 
   1267 
   1268 (defun vterm--backward-char ()
   1269   "Move point N characters backward.
   1270 
   1271 Return count of moved characeters."
   1272   (vterm-reset-cursor-point)
   1273   (let ((pt (point)))
   1274     (vterm-send-key "<left>" nil nil nil t)
   1275     (cond
   1276      ((= (point) (1- pt)) t)
   1277      ((and (= (point) (- pt 2))
   1278            ;; backward cross fake newline
   1279            (string-equal (buffer-substring-no-properties
   1280                           (1+ (point)) (+ 2 (point)))
   1281                          "\n"))
   1282       t)
   1283      ((and (= (point) (+ 4 pt))
   1284            (looking-back (regexp-quote "^[[D") nil)) ;escape code for <left>
   1285       (dotimes (_ 3) (vterm-send-key "<backspace>" nil nil nil t)) ;;delete  "^[[D"
   1286       nil)
   1287      (t nil))))
   1288 
   1289 (defun vterm--delete-region(start end)
   1290   "A wrapper for `delete-region'."
   1291   (funcall vterm--delete-region-function start end))
   1292 
   1293 (defun vterm--insert(&rest content)
   1294   "A wrapper for `insert'."
   1295   (apply vterm--insert-function content))
   1296 
   1297 (defun vterm--delete-char(n &optional killflag)
   1298   "A wrapper for `delete-char'."
   1299   (funcall vterm--delete-char-function n killflag))
   1300 
   1301 (defun vterm--translate-event-to-args (event &optional meta)
   1302   "Translate EVENT as list of args for `vterm-send-key'.
   1303 
   1304 When some input method is enabled, one key may generate
   1305 several characters, so the result of this function is a list,
   1306 looks like: ((\"m\" :shift ))"
   1307   (let* ((modifiers (event-modifiers event))
   1308          (shift (memq 'shift modifiers))
   1309          (meta (or meta (memq 'meta modifiers)))
   1310          (ctrl (memq 'control modifiers))
   1311          (raw-key (event-basic-type event))
   1312          (ev-keys) keys)
   1313     (if input-method-function
   1314         (let ((inhibit-read-only t))
   1315           (setq ev-keys (funcall input-method-function raw-key))
   1316           (when (listp ev-keys)
   1317             (dolist (k ev-keys)
   1318               (when-let ((key (key-description (vector k))))
   1319                 (when (and (not (symbolp event)) shift (not meta) (not ctrl))
   1320                   (setq key (upcase key)))
   1321                 (setq keys (append keys (list (list key shift meta ctrl))))))))
   1322       (when-let ((key (key-description (vector raw-key))))
   1323         (when (and (not (symbolp event)) shift (not meta) (not ctrl))
   1324           (setq key (upcase key)))
   1325         (setq keys  (list (list key shift meta ctrl)))))
   1326     keys))
   1327 
   1328 (defun vterm--invalidate ()
   1329   "The terminal buffer is invalidated, the buffer needs redrawing."
   1330   (if (and (not vterm--redraw-immididately)
   1331            vterm-timer-delay)
   1332       (unless vterm--redraw-timer
   1333         (setq vterm--redraw-timer
   1334               (run-with-timer vterm-timer-delay nil
   1335                               #'vterm--delayed-redraw (current-buffer))))
   1336     (vterm--delayed-redraw (current-buffer))
   1337     (setq vterm--redraw-immididately nil)))
   1338 
   1339 (defun vterm-check-proc (&optional buffer)
   1340   "Check if there is a running process associated to the vterm buffer BUFFER.
   1341 
   1342 BUFFER can be either a buffer or the name of one."
   1343   (let* ((buffer (get-buffer (or buffer (current-buffer))))
   1344          (proc (get-buffer-process buffer)))
   1345     (and proc
   1346          (memq (process-status proc) '(run stop open listen connect))
   1347          (buffer-local-value 'vterm--term buffer))))
   1348 
   1349 (defun vterm--delayed-redraw (buffer)
   1350   "Redraw the terminal buffer.
   1351 Argument BUFFER the terminal buffer."
   1352   (when (buffer-live-p buffer)
   1353     (with-current-buffer buffer
   1354       (let ((inhibit-redisplay t)
   1355             (inhibit-read-only t)
   1356             (windows (get-buffer-window-list)))
   1357         (setq vterm--redraw-timer nil)
   1358         (when vterm--term
   1359           (vterm--redraw vterm--term)
   1360           (unless (zerop (window-hscroll))
   1361             (when (cl-member (selected-window) windows :test #'eq)
   1362               (set-window-hscroll (selected-window) 0))))))))
   1363 
   1364 ;; see VTermSelectionMask in vterm.el
   1365 ;; VTERM_SELECTION_CLIPBOARD = (1<<0),
   1366 ;; VTERM_SELECTION_PRIMARY   = (1<<1),
   1367 (defconst vterm--selection-clipboard 1)   ;(1<<0)
   1368 (defconst vterm--selection-primary   2)   ;(1<<1)
   1369 (defun vterm--set-selection (mask data)
   1370   "OSC 52 Manipulate Selection Data.
   1371 Search Manipulate Selection Data in
   1372  https://invisible-island.net/xterm/ctlseqs/ctlseqs.html ."
   1373   (when vterm-enable-manipulate-selection-data-by-osc52
   1374     (let ((select-enable-clipboard select-enable-clipboard)
   1375           (select-enable-primary select-enable-primary))
   1376       (setq select-enable-clipboard
   1377             (logand mask vterm--selection-clipboard))
   1378       (setq select-enable-primary
   1379             (logand mask vterm--selection-primary))
   1380       (kill-new data)
   1381       (message "kill-ring is updated by vterm OSC 52(Manipulate Selection Data)"))
   1382     ))
   1383 
   1384 ;;; Entry Points
   1385 
   1386 ;;;###autoload
   1387 (defun vterm (&optional arg)
   1388   "Create an interactive Vterm buffer.
   1389 Start a new Vterm session, or switch to an already active
   1390 session.  Return the buffer selected (or created).
   1391 
   1392 With a nonnumeric prefix arg, create a new session.
   1393 
   1394 With a string prefix arg, create a new session with arg as buffer name.
   1395 
   1396 With a numeric prefix arg (as in `C-u 42 M-x vterm RET'), switch
   1397 to the session with that number, or create it if it doesn't
   1398 already exist.
   1399 
   1400 The buffer name used for Vterm sessions is determined by the
   1401 value of `vterm-buffer-name'."
   1402   (interactive "P")
   1403   (vterm--internal #'pop-to-buffer-same-window arg))
   1404 
   1405 ;;;###autoload
   1406 (defun vterm-other-window (&optional arg)
   1407   "Create an interactive Vterm buffer in another window.
   1408 Start a new Vterm session, or switch to an already active
   1409 session.  Return the buffer selected (or created).
   1410 
   1411 With a nonnumeric prefix arg, create a new session.
   1412 
   1413 With a string prefix arg, create a new session with arg as buffer name.
   1414 
   1415 With a numeric prefix arg (as in `C-u 42 M-x vterm RET'), switch
   1416 to the session with that number, or create it if it doesn't
   1417 already exist.
   1418 
   1419 The buffer name used for Vterm sessions is determined by the
   1420 value of `vterm-buffer-name'."
   1421   (interactive "P")
   1422   (vterm--internal #'pop-to-buffer arg))
   1423 
   1424 (defun vterm--internal (pop-to-buf-fun &optional arg)
   1425   (cl-assert vterm-buffer-name)
   1426   (let ((buf (cond ((numberp arg)
   1427                     (get-buffer-create (format "%s<%d>"
   1428                                                vterm-buffer-name
   1429                                                arg)))
   1430                    ((stringp arg) (generate-new-buffer arg))
   1431                    (arg (generate-new-buffer vterm-buffer-name))
   1432                    (t
   1433                     (get-buffer-create vterm-buffer-name)))))
   1434     (cl-assert (and buf (buffer-live-p buf)))
   1435     (funcall pop-to-buf-fun buf)
   1436     (with-current-buffer buf
   1437       (unless (derived-mode-p 'vterm-mode)
   1438         (vterm-mode)))
   1439     buf))
   1440 
   1441 ;;; Internal
   1442 
   1443 (defun vterm--flush-output (output)
   1444   "Send the virtual terminal's OUTPUT to the shell."
   1445   (process-send-string vterm--process output))
   1446 ;; Terminal emulation
   1447 ;; This is the standard process filter for term buffers.
   1448 ;; It emulates (most of the features of) a VT100/ANSI-style terminal.
   1449 
   1450 ;; References:
   1451 ;; [ctlseqs]: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
   1452 ;; [ECMA-48]: https://www.ecma-international.org/publications/standards/Ecma-048.htm
   1453 ;; [vt100]: https://vt100.net/docs/vt100-ug/chapter3.html
   1454 
   1455 (defconst vterm-control-seq-regexp
   1456   (concat
   1457    ;; A control character,
   1458    "\\(?:[\r\n\000\007\t\b\016\017]\\|"
   1459    ;; a C1 escape coded character (see [ECMA-48] section 5.3 "Elements
   1460    ;; of the C1 set"),
   1461    "\e\\(?:[DM78c=]\\|"
   1462    ;; another Emacs specific control sequence for term.el,
   1463    "AnSiT[^\n]+\n\\|"
   1464    ;; another Emacs specific control sequence for vterm.el
   1465    ;; printf "\e]%s\e\\"
   1466    "\\][^\e]+\e\\\\\\|"
   1467    ;; or an escape sequence (section 5.4 "Control Sequences"),
   1468    "\\[\\([\x30-\x3F]*\\)[\x20-\x2F]*[\x40-\x7E]\\)\\)")
   1469   "Regexp matching control sequences handled by term.el.")
   1470 
   1471 (defconst vterm-control-seq-prefix-regexp
   1472   "[\032\e]")
   1473 
   1474 (defun vterm--filter (process input)
   1475   "I/O Event.  Feeds PROCESS's INPUT to the virtual terminal.
   1476 
   1477 Then triggers a redraw from the module."
   1478   (let ((inhibit-redisplay t)
   1479         (inhibit-eol-conversion t)
   1480         (inhibit-read-only t)
   1481         (buf (process-buffer process))
   1482         (i 0)
   1483         (str-length (length input))
   1484         decoded-substring
   1485         funny)
   1486     (when (buffer-live-p buf)
   1487       (with-current-buffer buf
   1488         ;; borrowed from term.el
   1489         ;; Handle non-control data.  Decode the string before
   1490         ;; counting characters, to avoid garbling of certain
   1491         ;; multibyte characters (https://github.com/akermu/emacs-libvterm/issues/394).
   1492         ;; same bug of term.el https://debbugs.gnu.org/cgi/bugreport.cgi?bug=1006
   1493         (when vterm--undecoded-bytes
   1494           (setq input (concat vterm--undecoded-bytes input))
   1495           (setq vterm--undecoded-bytes nil)
   1496           (setq str-length (length input)))
   1497         (while (< i str-length)
   1498           (setq funny (string-match vterm-control-seq-regexp input i))
   1499           (let ((ctl-end (if funny (match-end 0)
   1500                            (setq funny (string-match vterm-control-seq-prefix-regexp input i))
   1501                            (if funny
   1502                                (setq vterm--undecoded-bytes
   1503                                      (substring input funny))
   1504                              (setq funny str-length))
   1505                            ;; The control sequence ends somewhere
   1506                            ;; past the end of this string.
   1507                            (1+ str-length))))
   1508             (when (> funny i)
   1509               ;; Handle non-control data.  Decode the string before
   1510               ;; counting characters, to avoid garbling of certain
   1511               ;; multibyte characters (emacs bug#1006).
   1512               (setq decoded-substring
   1513                     (decode-coding-string
   1514                      (substring input i funny)
   1515                      locale-coding-system t))
   1516               ;; Check for multibyte characters that ends
   1517               ;; before end of string, and save it for
   1518               ;; next time.
   1519               (when (= funny str-length)
   1520                 (let ((partial 0)
   1521                       (count (length decoded-substring)))
   1522                   (while (and (< partial count)
   1523                               (eq (char-charset (aref decoded-substring
   1524                                                       (- count 1 partial)))
   1525                                   'eight-bit))
   1526                     (cl-incf partial))
   1527                   (when (> (1+ count) partial 0)
   1528                     (setq vterm--undecoded-bytes
   1529                           (substring decoded-substring (- partial)))
   1530                     (setq decoded-substring
   1531                           (substring decoded-substring 0 (- partial)))
   1532                     (cl-decf str-length partial)
   1533                     (cl-decf funny partial))))
   1534               (ignore-errors (vterm--write-input vterm--term decoded-substring))
   1535               (setq i funny))
   1536             (when (<= ctl-end str-length)
   1537               (ignore-errors (vterm--write-input vterm--term (substring input i ctl-end))))
   1538             (setq i ctl-end)))
   1539         (vterm--update vterm--term)))))
   1540 
   1541 (defun vterm--sentinel (process event)
   1542   "Sentinel of vterm PROCESS.
   1543 Argument EVENT process event."
   1544   (let ((buf (process-buffer process)))
   1545     (run-hook-with-args 'vterm-exit-functions
   1546                         (if (buffer-live-p buf) buf nil)
   1547                         event)
   1548     (if (and vterm-kill-buffer-on-exit (buffer-live-p buf))
   1549         (kill-buffer buf))))
   1550 
   1551 (defun vterm--text-scale-mode (&optional _argv)
   1552   "Fix `line-number' height for scaled text."
   1553   (and text-scale-mode
   1554        (or (equal major-mode 'vterm-mode)
   1555            (derived-mode-p 'vterm-mode))
   1556        (boundp 'display-line-numbers)
   1557        (let ((height (expt text-scale-mode-step
   1558                            text-scale-mode-amount)))
   1559          (when vterm--linenum-remapping
   1560            (face-remap-remove-relative vterm--linenum-remapping))
   1561          (setq vterm--linenum-remapping
   1562                (face-remap-add-relative 'line-number :height height))))
   1563   (window--adjust-process-windows))
   1564 
   1565 (advice-add #'text-scale-mode :after #'vterm--text-scale-mode)
   1566 
   1567 (defun vterm--window-adjust-process-window-size (process windows)
   1568   "Adjust width of window WINDOWS associated to process PROCESS.
   1569 
   1570 `vterm-min-window-width' determines the minimum width allowed."
   1571   ;; We want `vterm-copy-mode' to resemble a fundamental buffer as much as
   1572   ;; possible.  Hence, we must not call this function when the minor mode is
   1573   ;; enabled, otherwise the buffer would be redrawn, messing around with the
   1574   ;; position of the point.
   1575   (unless vterm-copy-mode
   1576     (let* ((size (funcall window-adjust-process-window-size-function
   1577                           process windows))
   1578            (width (car size))
   1579            (height (cdr size))
   1580            (inhibit-read-only t))
   1581       (setq width (- width (vterm--get-margin-width)))
   1582       (setq width (max width vterm-min-window-width))
   1583       (when (and (processp process)
   1584                  (process-live-p process)
   1585                  (> width 0)
   1586                  (> height 0))
   1587         (vterm--set-size vterm--term height width)
   1588         (cons width height)))))
   1589 
   1590 (defun vterm--get-margin-width ()
   1591   "Get margin width of vterm buffer when `display-line-numbers-mode' is enabled."
   1592   (let ((width 0)
   1593         (max-line-num (+ (frame-height) vterm-max-scrollback)))
   1594     (when (bound-and-true-p display-line-numbers)
   1595       (setq width (+ width 4
   1596                      (string-width (number-to-string max-line-num)))))
   1597     width))
   1598 
   1599 (defun vterm--delete-lines (line-num count &optional delete-whole-line)
   1600   "Delete COUNT lines from LINE-NUM.
   1601 If LINE-NUM is negative backward-line from end of buffer.
   1602 If option DELETE-WHOLE-LINE is non-nil, then this command kills
   1603 the whole line including its terminating newline"
   1604   (save-excursion
   1605     (when (vterm--goto-line line-num)
   1606       (vterm--delete-region (point) (line-end-position count))
   1607       (when (and delete-whole-line
   1608                  (looking-at "\n"))
   1609         (vterm--delete-char 1)))))
   1610 
   1611 (defun vterm--goto-line (n)
   1612   "Go to line N and return true on success.
   1613 If N is negative backward-line from end of buffer."
   1614   (cond
   1615    ((> n 0)
   1616     (goto-char (point-min))
   1617     (eq 0 (forward-line (1- n))))
   1618    (t
   1619     (goto-char (point-max))
   1620     (eq 0 (forward-line n)))))
   1621 
   1622 (defun vterm--set-title (title)
   1623   "Use TITLE to set the buffer name according to `vterm-buffer-name-string'."
   1624   (when vterm-buffer-name-string
   1625     (rename-buffer (format vterm-buffer-name-string title) t)))
   1626 
   1627 (defun vterm--set-directory (path)
   1628   "Set `default-directory' to PATH."
   1629   (let ((dir (vterm--get-directory path)))
   1630     (when dir (setq default-directory dir))))
   1631 
   1632 (defun vterm--get-directory (path)
   1633   "Get normalized directory to PATH."
   1634   (when path
   1635     (let (directory)
   1636       (if (string-match "^\\(.*?\\)@\\(.*?\\):\\(.*?\\)$" path)
   1637           (progn
   1638             (let ((user (match-string 1 path))
   1639                   (host (match-string 2 path))
   1640                   (dir (match-string 3 path)))
   1641               (if (and (string-equal user user-login-name)
   1642                        (string-equal host (system-name)))
   1643                   (progn
   1644                     (when (file-directory-p dir)
   1645                       (setq directory (file-name-as-directory dir))))
   1646                 (setq directory (file-name-as-directory (concat "/-:" path))))))
   1647         (when (file-directory-p path)
   1648           (setq directory (file-name-as-directory path))))
   1649       directory)))
   1650 
   1651 (defun vterm--get-pwd (&optional linenum)
   1652   "Get working directory at LINENUM."
   1653   (when vterm--term
   1654     (let ((raw-pwd (vterm--get-pwd-raw
   1655                     vterm--term
   1656                     (or linenum (line-number-at-pos)))))
   1657       (when raw-pwd
   1658         (vterm--get-directory raw-pwd)))))
   1659 
   1660 (defun vterm--get-color (index &rest args)
   1661   "Get color by INDEX from `vterm-color-palette'.
   1662 
   1663 Special INDEX of -1 is used to represent default colors.  ARGS
   1664 may optionally contain `:underline' or `:inverse-video' for cells
   1665 with underline or inverse video attribute.  If ARGS contains
   1666 `:foreground', use foreground color of the respective face
   1667 instead of background."
   1668   (let ((foreground    (member :foreground args))
   1669         (underline     (member :underline args))
   1670         (inverse-video (member :inverse-video args)))
   1671     (funcall (if foreground #'face-foreground #'face-background)
   1672              (cond
   1673               ((and (>= index 0) (< index 16))
   1674                (elt vterm-color-palette index))
   1675               ((and (= index -1) foreground underline)
   1676                'vterm-color-underline)
   1677               ((and (= index -1) (not foreground) inverse-video)
   1678                'vterm-color-inverse-video)
   1679               (t 'default))
   1680              nil 'default)))
   1681 
   1682 (defun vterm--eval (str)
   1683   "Check if string STR is `vterm-eval-cmds' and execute command.
   1684 
   1685 All passed in arguments are strings and forwarded as string to
   1686 the called functions."
   1687   (let* ((parts (split-string-and-unquote str))
   1688          (command (car parts))
   1689          (args (cdr parts))
   1690          (f (assoc command vterm-eval-cmds)))
   1691     (if f
   1692         (apply (cadr f) args)
   1693       (message "Failed to find command: %s.  To execute a command,
   1694                 add it to the `vterm-eval-cmd' list" command))))
   1695 
   1696 ;; TODO: Improve doc string, it should not point to the readme but it should
   1697 ;;       be self-contained.
   1698 (defun vterm--prompt-tracking-enabled-p ()
   1699   "Return t if tracking the prompt is enabled.
   1700 
   1701 Prompt tracking need shell side configurations.
   1702 
   1703 For zsh user, this is done by PROMPT=$PROMPT'%{$(vterm_prompt_end)%}'.
   1704 
   1705 The shell send semantic information about where the prompt ends via properly
   1706 escaped sequences to Emacs.
   1707 
   1708 More information see `Shell-side configuration' and `Directory tracking'
   1709 in README."
   1710   (or vterm--prompt-tracking-enabled-p
   1711       (save-excursion
   1712         (setq vterm--prompt-tracking-enabled-p
   1713               (next-single-property-change (point-min) 'vterm-prompt)))))
   1714 
   1715 (defun vterm-next-prompt (n)
   1716   "Move to end of Nth next prompt in the buffer."
   1717   (interactive "p")
   1718   (if (and vterm-use-vterm-prompt-detection-method
   1719            (vterm--prompt-tracking-enabled-p))
   1720       (let ((pt (point))
   1721             (promp-pt (vterm--get-prompt-point)))
   1722         (when promp-pt (goto-char promp-pt))
   1723         (cl-loop repeat (or n 1) do
   1724                  (setq pt (next-single-property-change (line-beginning-position 2) 'vterm-prompt))
   1725                  (when pt (goto-char pt))))
   1726     (term-next-prompt n)))
   1727 
   1728 (defun vterm-previous-prompt (n)
   1729   "Move to end of Nth previous prompt in the buffer."
   1730   (interactive "p")
   1731   (if (and vterm-use-vterm-prompt-detection-method
   1732            (vterm--prompt-tracking-enabled-p))
   1733       (let ((pt (point))
   1734             (prompt-pt (vterm--get-prompt-point)))
   1735         (when prompt-pt
   1736           (goto-char prompt-pt)
   1737           (when (> pt (point))
   1738             (setq n (1- (or n 1))))
   1739           (cl-loop repeat n do
   1740                    (setq pt (previous-single-property-change (1- (point)) 'vterm-prompt))
   1741                    (when pt (goto-char (1- pt))))))
   1742     (term-previous-prompt n)))
   1743 
   1744 (defun vterm--get-beginning-of-line (&optional pt)
   1745   "Find the start of the line, bypassing line wraps.
   1746 If PT is specified, find it's beginning of the line instead of the beginning
   1747 of the line at cursor."
   1748   (save-excursion
   1749     (when pt (goto-char pt))
   1750     (beginning-of-line)
   1751     (while (and (not (bobp))
   1752                 (get-text-property (1- (point)) 'vterm-line-wrap))
   1753       (forward-char -1)
   1754       (beginning-of-line))
   1755     (point)))
   1756 
   1757 (defun vterm--get-end-of-line (&optional pt)
   1758   "Find the start of the line, bypassing line wraps.
   1759 If PT is specified, find it's end of the line instead of the end
   1760 of the line at cursor."
   1761   (save-excursion
   1762     (when pt (goto-char pt))
   1763     (end-of-line)
   1764     (while (get-text-property (point) 'vterm-line-wrap)
   1765       (forward-char)
   1766       (end-of-line))
   1767     (point)))
   1768 
   1769 ;; TODO: Improve doc string, it should not point to the readme but it should
   1770 ;;       be self-contained.
   1771 (defun vterm--get-prompt-point ()
   1772   "Get the position of the end of current prompt.
   1773 More information see `vterm--prompt-tracking-enabled-p' and
   1774 `Directory tracking and Prompt tracking'in README."
   1775   (let ((end-point (vterm--get-end-of-line))
   1776         prompt-point)
   1777     (save-excursion
   1778       (if (and vterm-use-vterm-prompt-detection-method
   1779                (vterm--prompt-tracking-enabled-p))
   1780           (if (get-text-property end-point 'vterm-prompt)
   1781               end-point
   1782             (setq prompt-point (previous-single-property-change end-point 'vterm-prompt))
   1783             (when prompt-point (setq prompt-point (1- prompt-point))))
   1784         (goto-char end-point)
   1785         (if (search-backward-regexp term-prompt-regexp nil t)
   1786             (goto-char (match-end 0))
   1787           (vterm--get-beginning-of-line))))))
   1788 
   1789 (defun vterm--at-prompt-p ()
   1790   "Return t if the cursor position is at shell prompt."
   1791   (= (point) (or (vterm--get-prompt-point) 0)))
   1792 
   1793 (defun vterm-cursor-in-command-buffer-p (&optional pt)
   1794   "Check whether cursor in command buffer area."
   1795   (save-excursion
   1796     (vterm-reset-cursor-point)
   1797     (let ((promp-pt (vterm--get-prompt-point)))
   1798       (when promp-pt
   1799         (<= promp-pt (or pt (vterm--get-cursor-point)))))))
   1800 
   1801 (defun vterm-beginning-of-line ()
   1802   "Move point to the beginning of the line.
   1803 
   1804 Move the point to the first character after the shell prompt on this line.
   1805 If the point is already there, move to the beginning of the line.
   1806 Effectively toggle between the two positions."
   1807   (interactive "^")
   1808   (if (vterm--at-prompt-p)
   1809       (goto-char (vterm--get-beginning-of-line))
   1810     (goto-char (max (or (vterm--get-prompt-point) 0)
   1811                     (vterm--get-beginning-of-line)))))
   1812 
   1813 (defun vterm-end-of-line ()
   1814   "Move point to the end of the line, bypassing line wraps."
   1815   (interactive "^")
   1816   (goto-char (vterm--get-end-of-line)))
   1817 
   1818 (defun vterm-reset-cursor-point ()
   1819   "Make sure the cursor at the right position."
   1820   (interactive)
   1821   (when vterm--term
   1822     (let ((inhibit-read-only t))
   1823       (vterm--reset-point vterm--term))))
   1824 
   1825 (defun vterm--get-cursor-point ()
   1826   "Get term cursor position."
   1827   (when vterm--term
   1828     (save-excursion
   1829       (vterm-reset-cursor-point))))
   1830 
   1831 (defun vterm--reinsert-fake-newlines ()
   1832   "Reinsert fake newline from `vterm--copy-mode-fake-newlines'."
   1833   (let ((inhibit-read-only t)
   1834         (inhibit-redisplay t)
   1835         (fake-newline-text "\n")
   1836         fake-newline-pos)
   1837     (add-text-properties 0 1 '(vterm-line-wrap t rear-nonsticky t)
   1838                          fake-newline-text)
   1839     (while vterm--copy-mode-fake-newlines
   1840       (setq fake-newline-pos (car vterm--copy-mode-fake-newlines))
   1841       (setq vterm--copy-mode-fake-newlines (cdr vterm--copy-mode-fake-newlines))
   1842       (goto-char fake-newline-pos)
   1843       (insert fake-newline-text))))
   1844 
   1845 (defun vterm--remove-fake-newlines (&optional remembering-pos-p)
   1846   "Filter out injected newlines were injected when rendering the terminal.
   1847 
   1848 These newlines were tagged with \\='vterm-line-wrap property so we
   1849 can find them and remove them.
   1850 If REMEMBERING-POS-P is not nil remembering their positions in a buffer-local
   1851 `vterm--copy-mode-fake-newlines'."
   1852   (let (fake-newline
   1853         (inhibit-read-only t)
   1854         (inhibit-redisplay t))
   1855     (when remembering-pos-p
   1856       (setq vterm--copy-mode-fake-newlines nil))
   1857 
   1858     (goto-char (point-max))
   1859     (when (and (bolp)
   1860                (not (bobp))
   1861                (get-text-property (1- (point)) 'vterm-line-wrap))
   1862       (forward-char -1)
   1863       (when remembering-pos-p
   1864         (setq vterm--copy-mode-fake-newlines
   1865               (cons (point) vterm--copy-mode-fake-newlines)))
   1866       (vterm--delete-char 1))
   1867 
   1868     (while (and (not (bobp))
   1869                 (setq fake-newline (previous-single-property-change
   1870                                     (point) 'vterm-line-wrap)))
   1871       (goto-char (1- fake-newline))
   1872       (cl-assert (eq ?\n (char-after)))
   1873       (when remembering-pos-p
   1874         (setq vterm--copy-mode-fake-newlines
   1875               (cons (point) vterm--copy-mode-fake-newlines)))
   1876       (vterm--delete-char 1))))
   1877 
   1878 (defun vterm--filter-buffer-substring (content)
   1879   "Filter string CONTENT of fake/injected newlines."
   1880   (with-temp-buffer
   1881     (vterm--insert content)
   1882     (vterm--remove-fake-newlines nil)
   1883     (buffer-string)))
   1884 
   1885 
   1886 (provide 'vterm)
   1887 ;; Local Variables:
   1888 ;; indent-tabs-mode: nil
   1889 ;; End:
   1890 ;;; vterm.el ends here