Backspace character weirdness - text

I wonder why backspace character in common Linux terminals does not actually erase the characters, when printed (which normally works when typed)..
This works as expected:
$ echo -e "abc\b\b\bxyz"
xyz
(\b evaluates to backspace, can be inserted also as Ctrl+V Ctrl+H - rendered as ^H (0x08))
but when there are less characters after the backspaces, the strange behavior is revealed:
$ echo -e "abc\b\b\bx"
xbc
it behaves like left arrow keys instead of backspace:
$ echo -e "abc\e[D\e[D\e[Dx"
xbc
erase line back works normally:
$ echo -e "abc\e[1Kx"
x
In fact, when I type Ctrl+V Backspace in terminal, ^? (0x7f) is yielded instead of ^H, this is Del ascii character, but Ctrl+V Del produces <ESC>[3~, but it is another story..
So can someone explain why printed backspace character does not erase the characters?
(My environment is xterm Linux and some other terminal emulators, $TERM == xterm, tried vt100, Linux as well)

What you are seeing is correct. Backspace or ^H moves the cursor to the left, no erasing. To erase a character, you need to output ^H ^H (Backspace-Space-Backspace).
To answer your comment - Backspace is defined that way in the VT100/ANSI family of terminals, from which a lot of terminal control code sequences borrow. See the VT100 user manual here which defines the function of BS as "Moves cursor to the left one character position, unless it is at the left margin, in which case no action occurs". In other words it's a quirk of history :)
As to why it was defined this way initially - I guess it's more flexible to have a non destructive cursor movement control code, as destructive backspace can be implemented as shown above.

Related

Terminal: send a do-nothing escape sequence or control character

I need my terminal to send an unused control character or escape sequence, one that has no effect at all layers: ignored by shells (bash, ...), ignored by line-editors (readline, ...), and ignored by all applications (vim, less, mutt, ...). I'll then bind this key within tmux, with a user defined key escape sequence if necessary. What control character or escape sequence do I use? More information below:
I want the Control-Shift-c key chord in tmux bound to an action which will copy the tmux selection into the X clipboard selection buffer. That and continue to have Control-Shift-c copy the terminal selection into the X clipboard selection buffer when tmux is not running. Terminal emulators generate the same output for both Control-Shift-key and Control-key inputs, see [1][2]. The first step is to change this:
# Enable fixterms (I think) sequences for all keys:
xterm -xrm "XTerm.vt100.modifyOtherKeys: 2" -xrm "XTerm.vt100.formatOtherKeys: 1"
This instructs xterm to construct an escape sequence for all keys modified by Control, Alt, or Meta. As far as I can tell nothing supports these escape sequence, whether they're formatted using the original xterm sequences or via the new fixterms [3] specifications. Even tmux supports only a subset of these sequences [4], rather than a full-blown CSI-sequence parser [5].
The simplest workaround is to have only Control-Shift-c send a fixterms sequence, as per [6]. Since this sequence isn't supported by tmux, it'll have to be manually defined via the user-keys option. It will also have to be bound in the root key table of tmux rather than one of the copy-mode tables; otherwise if tmux isn't in copy-mode the binding will be ignored and passed through tmux to one of the terminal applications.
# Configure only Control-Shift-c to send a fixterms sequence:
xterm -xrm "XTerm*vt100.translations: #override \n\
Ctrl Shift <Key>c: string(0x1b) string ([67;6u)"
# Recognize (but don't handle) the Control-Shift-c fixterms sequence:
tmux set-option -s user-keys[0] "\e[67;6u"
# Copy the selection to the clipboard buffer only when in copy-mode. If
# there is no selection, nothing will be copied:
tmux bind-key -T root User0 if-shell -Ft= "#{pane_in_mode}"
"send-keys -X copy-pipe 'xsel -i -b'"
All other applications, which don't support fixterms sequences, will receive input junk. Even worse, unknown escape sequences are likely to be misinterpreted and trigger application-specific commands. Initially I considered using tcgetpgrp(3)[7] to get the name of the command currently running within the terminal, much like #{pane_current_command} in tmux[8].
xterm <-> bash <-> command
The terminal binding for Control-Shift-c would first copy the terminal's selection to the clipboard buffer, as usual; then call my external program[9]. If the terminal command isn't currently tmux, nothing happens; otherwise the external command writes the fixterms Control-Shift-c sequence to the terminal's pts. When tmux receives that sequence it will overwrite the clipboard buffer with its own selection.
xterm -xrm "XTerm*vt100.translations: #override \n\
Ctrl Shift <Key>c: copy-selection(PRIMARY) \n\
exec-formatted("~/send_fixterms_sequence_if_tmux.py")
This fails to handle nested terminal emulators, as when running tmux over ssh - which is very common.
xterm <-> bash <-> ssh <-> bash <-> tmux <-> bash <-> command
That's my dilemma and I'm currently considering several alternatives:
Have the terminal send a control character rather than an escape sequence. Control characters are always supported. I'd like there to exist a do-nothing character and was hopeful for Control-# (NUL or ASCII 0), but that character is echoed by the shell and has a significant effect in insert mode in vim. If no such character exists, see #3. Perhaps I could appropriate an uncommon control character, but it would also have to be configured to do nothing, across all layers: xterm, bash, readline, vim, etc.
Have the terminal send an unused or do-nothing escape sequence rather than the Control-Shift-c fixterms sequence. The sequence would need be ignored at all layers: ignored by shells (bash, ...), ignored by line-editors (readline, ...), and ignored by all applications (vim, less, mutt, ...). See #3.
Modify the terminfo entry for my terminal to ensure that at least one of the above (control code, standard escape sequence, or fixterms escape sequence) is ignored at all layers, as per [10]. Then, bind this modified sequence in tmux.
Invoke readline to do something magical. A long-shot as this is unlikely to have any effect on alternate-mode terminal applications.
The idea is to, as before, copy the terminal's selection to the clipboard buffer. Then insert <STRING> as if it had been typed. When tmux receives <STRING> it will overwrite the clipboard buffer with its own selection. Any other application will ignore it: including and especially having nothing printed to the terminal.
xterm -xrm "XTerm*vt100.translations: #override \n\
Ctrl Shift <Key>c: copy-selection(PRIMARY) string(<STRING>)
I'm also planning on extending this to gnome-terminal, so an example of writing the escape sequence or control character to the terminal's pts would be appreciated. I only use xterm as a working example - this question is definitely not xterm-specific.
https://stackoverflow.com/a/14876639
https://unix.stackexchange.com/a/116630
http://www.leonerd.org.uk/hacks/fixterms/
https://github.com/tmux/tmux/blob/master/xterm-keys.c
http://www.leonerd.org.uk/code/libtermkey/
https://stackoverflow.com/a/2179779
http://man7.org/linux/man-pages/man3/tcgetpgrp.3.html
https://github.com/tmux/tmux/blob/master/osdep-linux.c
https://invisible-island.net/xterm/manpage/xterm.html#h2-KEY-BINDINGS
Bind Ctrl+Tab and Ctrl+Shift+Tab in tmux
https://man.openbsd.org/ssh#ESCAPE_CHARACTERS
edit, new idea: SSH provides its own terminal emulator, or at least is hooked up to a pts pair[11]. Does that mean it can handle incoming escape sequences, and perhaps run an external remote command, such as one using tcgetpgrp? Or would that be an insecurity? Rather than configuring a possibly endless series of terminal applications to ignore an escape sequence, I'd prefer to configure only SSH as I would tmux.
All the available sequences will potentially look like a key of some kind to the application receiving it (for example, 0 is C-# and C-Space), there is no sequence that applications are guaranteed to ignore.
If I was you I'd just send the escape sequence for F20 or something and bind that key to do nothing in the other applications you use a lot.

Vim's normal command doesn't work correctly in command mode

Input:
==abc==
===abc===
====abc====
Vim's command mode → :g/=$/normal b i InsertedtexT
(aliases: "norm" and "norm!") → Output:
==abc==
InsertedtexT===abc===
InsertedtexT====abc====
Normal mode → $ b i InsertedtexT → Output (this is what it should do):
==abcInsertedtexT==
===abcInsertedtexT===
====abcInsertedtexT====
:g will not position the cursor at the match, but at the start of the line. And space will move a cursor right (which I assume you did not type when testing in normal mode.) So this happens: :g finds a line that ends with equals; b takes you to the previous line (except for in the first line, where the execution of :norm is aborted as you can't go backwards), so now the cursor is in between lines; space moves you forward, to the beginning of the next line; i enters insert mode; and InsertedtexT gets inserted part-and-parcel, together with the leading space.
What you wanted to do is exactly what you have done in normal mode:
:g/=$/norm! $biInsertedtexT
(note no spaces, and note that you need to explicitly go to the end of the line; and :norm! just in case, if your environment has any weird mapping going on.)
tl;dr: Vim's normal command does work correctly in command mode.

How do I bind C-? in bash?

I want to bind C-? to history-search-backward in bash and/or zsh. I tried the following way
bind '"^?": history-search-backward' # bash
bindkey '^?' history-search-backward # zsh
The binding works fine, but at the same time BS (backspace) stops working correctly. The reason is that BS generates the same code as C-? what one can check with C-v BS and C-v C-?. Thus the final result is that both C-? and BS are bound to history-search-backward what is obviously not what I wanted.
So the question is how can I properly bind C-? without affecting BS?
Which string is sent to the shell when pressing a key or key combination is determined by the terminal.
It seems that a lot (most?) terminal emulators by default send ^? (ASCII 0x7f, Delete) when pressing the backspace key. While the actual backspace character would be ^H (ASCII 0x08, Backspace).
If you can change the behaviour of your terminal emulator to send ^H you should be able to maintain the function of the backspace key while still being able to bind ^? to something else.
Terminal emulators the provide a GUI for configuration (gnome-terminal, roxterm, konsole, ...) should have an option for that.
Others (xterm, urxvt, ...) may have appropriate X resources that may be set in ~/.Xdefaults (or - depending on your distribution - ~/.Xresources).
For xterm you need to set:
XTerm.backarrowKeyIsErase: False
For urxvt set
URxvt.backspacekey: ^H
please note that this is the actual ^H (ASCII 0x08) character and not a two character string consisting of ^ and H.
To load these settings either call xrdb -merge ~/.Xdefaults or close and restart your X session. (The settings will only be enabled for new terminals)

Unable to replace a space with a new line in Vim

I know the thread.
I run
:%s/ /s/\n/g
I get
E488: Trailing characters
2nd example
I run
:%s/ /\n/g
I get
text^#text
I run the same codes also with the following settings separetaly
set fileformat=unix
and
set fileformat=dos
How can you replace with a new line in Vim?
:%s/ /Ctrl vReturn/g
Where Ctrl v is Control-key plus key v and Return is the return key (the one on the main keyboard, not the enter key on the numpad). The other characters are typed as usual.
If this is entered correctly, the sequence Ctrl vReturn will display as the characters ^M, typically in a different color, to indicate that they are special. Note that actually typing ^M will not work.
Also note that in Vim for windows, it's Control-q instead of Control-v (as that is paste).
Ctrl-v also allows entering other "special" keys via the keyboard. It is also useful for e.g. Tab or Backspace.
Try
%s/ /\r/g
Enter the following:
:s/ /
and now type Ctrl-V or Ctrl-Q (depends on your configuration) and hit the Enter key. You should now have:
:s/ /^M
Finish it off:
:s/ /^M/g
and you are good to go.
Specifically to answer your problem with trailing characters, this is the regex you specified:
:%s/ /s/\n/g
You have too many /. What happens is that you replace ' ' with s, and then you tag on this after the substitution: \n/g
I think you meant this:
:%s/ \s/\n/g
Note that your /s was changed to \s. Now the substitution will replace one space followed by one whitespace of any kind (space or tab) with \n. I doubt if this solve the problem or replacing space with a newline, but it should explain the error message.
Try either
For Unix:
:1,$s/\ /\n/g
For Windows:
:1,$s/\ /\r/g
This contains an escape character for the space.

How do I move to end of line in Vim?

I know how to generally move around in command mode, specifically, jumping to lines, etc. But what is the command to jump to the end of the line that I am currently on?
Just the $ (dollar sign) key. You can use A to move to the end of the line and switch to editing mode (Append). To jump to the last non-blank character, you can press g then _ keys.
The opposite of A is I (Insert mode at beginning of line), as an aside. Pressing just the ^ will place your cursor at the first non-white-space character of the line.
As lots of people have said:
$ gets you to the end of the line
but also:
^ or _ gets you to the first non-whitespace character in the line, and
0 (zero) gets you to the beginning of the line incl. whitespace
$ moves to the last character on the line.
g _ goes to the last non-whitespace character.
g $ goes to the end of the screen line (when a buffer line is wrapped across multiple screen lines)
The main question - end of line
$ goes to the end of line, remains in command mode
A goes to the end of line, switches to insert mode
Conversely - start of line (technically the first non-whitespace character)
^ goes to the start of line, remains in command mode
I (uppercase i) goes to the start of line, switches to insert mode
Further - start of line (technically the first column irrespective of whitespace)
0 (zero) goes to the start of line, remains in command mode
0i (zero followed by lowercase i) goes the start of line, switches to insert mode
For those starting to learn vi, here is a good introduction to vi by listing side by side vi commands to typical Windows GUI Editor cursor movement and shortcut keys.
vi editor for Windows users
If your current line wraps around the visible screen onto the next line, you can use g$ to get to the end of the screen line.
I can't see hotkey for macbook for use vim in standard terminal. Hope it will help someone.
For macOS users (tested on macbook pro 2018):
fn + ← - move to beginning line
fn + → - move to end line
fn + ↑ - move page up
fn + ↓ - move page down
fn + g - move the cursor to the beginning of the document
fn + shift + g - move the cursor to the end of the document
For the last two commands sometime needs to tap twice.
The dollar sign: $
Press A to enter edit mode starting at the end of the line.
The advantage of the 'End' key is it works in both normal and insert modes.
'$' works in normal/command mode only but it also works in the classic vi editor (good to know when vim is not available).
Also note the distinction between line (or perhaps physical line) and screen line. A line is terminated by the End Of Line character ("\n"). A screen line is whatever happens to be shown as one row of characters in your terminal or in your screen. The two come apart if you have physical lines longer than the screen width, which is very common when writing emails and such.
The distinction shows up in the end-of-line commands as well.
$ and 0 move to the end or beginning of the physical line or paragraph, respectively:
g$ and g0 move to the end or beginning of the screen line or paragraph, respectively.
If you always prefer the latter behavior, you can remap the keys like this:
:noremap 0 g0
:noremap $ g$
In many cases, when we are inside a string we are enclosed by a double quote, or while writing a statement we don't want to press escape and go to end of that line with arrow key and press the semicolon(;) just to end the line. Write the following line inside your vimrc file:
imap <C-l> <Esc>$a
What does the line say? It maps Ctrl+l to a series of commands. It is equivalent to you pressing Esc (command mode), $ (end of line), a (append) at once.
Or there's the obvious answer: use the End key to go to the end of the line.
Possibly unrelated, but if you want to start a new line after the current line, you can use o anywhere in the line.
The easiest option would be to key in $. If you are working with blocks of text, you might appreciate the command { and } in order to move a paragraph back and forward, respectively.
I was used to Home/End getting me to the start and end of lines in Insert mode (from use in Windows and I think Linux), which Mac doesn't support. This is particularly annoying because when I'm using vim on a remote system, I also can't easily do it. After some painful trial and error, I came up with these .vimrc lines which do the same thing, but bound to Ctrl-A for the start of the line and Ctrl-D for the end of the line. (For some reason, Ctrl-E I guess is reserved or at least I couldn't figure a way to bind it.) Enjoy.
:imap <Char-1> <Char-15>:normal 0<Char-13>
:imap <Char-4> <Char-15>:normal $<Char-13>
There's a good chart here for the ASCII control character codes here for others as well:
http://www.physics.udel.edu/~watson/scen103/ascii.html
You can also do Ctrl-V + Ctrl- as well, but that doesn't paste as well to places like this.

Resources