How can I convert tab to space in vim? - vim

What I want is converting tab key to space in vim editor.
For example if I enter [tab] key, then it converts to 4 spaces.
I added the following setting in .vimrc
set tabstop=4
set expandtab
set shiftwidth=4
When I enter the first tab, it moves the cursor to position 5 not position 4.
But from the second tab, cursor moves to the position 9, 13, 17 and so on.
Only the case of the first tab seems not correct.
What did I do wrong?

Columns are counted starting from 1.
Two tabs by four spaces each gives 8 spaces in total. But the editor is still in Insert mode, so the cursor gets positioned to column 9 (the one which doesn't exist yet) to allow to input the next char right after the tabs.
And so it is all right.

Related

How to fix problem with tabstop configuration in ~/. vimrc

I set tab configuration in ~/.vimrc as below:
set ts=4 sts=4 sw=4
I notice that if the word is 4 characters long or above, the cursor shift into right 4 spaces as in configuration for tabstop.
But if the word is less than 4 characters long, it didn't shift into 4 spaces.
Example:
'name' + <Tab>: tab produced correct number of spaces (i.e 4 spaces)
'age' + <Tab>: tab produced wrong number of spaces (i.e 1 space only)
Why is it ?
Does the word length effect tab?
What can I do if I want to shift the cursor to 4 spaces as configured regardless of the word length?
Thanks a lot
You’re probably inserting regular tabs, which display variable-width according to what’s before and after. I find having set list on is really handy for this (though you probably won’t like the default listchars settings).
If you really want spaces (which I find better anyway, set expandtab.
Also, most long-time users recommend leaving tabstop at 8, since you can’t control how wide every one’s tabs are.
The way that the tabstop, shiftwidth, and softtabstop options work is that they control indentation to certain points that are commonly referred to as "tab stops". In other words, they're designed to always indent to a column that's a multiple of the setting.
So if your tab stops are at multiples of 4, then hitting the Tab key will cause the cursor to indent to a column that is the next multiple of 4. This is the behavior of inserting a literal tab (U+0009, CHARACTER TABULATION) into a document and then rendering it on a normal terminal (except that the width is usually 8 there). This results in text that is aligned at fixed columns, which is the desired style for most programming languages and text markup formats.
As you've noted, this does result in different amounts of indent if the words are different lengths. Typically in code, we would just cause the second column to be at the next tab stop and not care that the indents are of different lengths. That is, in your example, we'd hit Tab once on the first line and twice on the second, and start the next column at column 8.
I'm not aware of any way to force Vim to insert a specific number of spaces other than the standard editing commands. Normally users who are in this situation just hit Space four times if they really want four spaces and not an indentation to the next tab stop. You can of course create a mapping if you need to do that a lot.

Why TAB inside VIM moves one space after exactly three-long characters words?

After one-character length word or more than three-length characters word there TAB moves exactly 4 spaces.
My .vimrc configuration file is it as it follows.
syntax on
" number of spaces moved along by pressing >>, << or ==
set shiftwidth=4
" number of spaces moved along by pressing the <TAB> or <BS> key
set softtabstop=4
set expandtab
The tabstop configuration value is set to default, 8.
I was expecting that TAB will move forward 4 spaces no matter what.
When indenting, vim inserts however many spaces it needs to get to the next multiple of shiftwidth.
In your case, shiftwidth=4, so vim inserts spaces until it gets to column 4: only one space.
If you want vim to instead blindly insert four spaces no matter what, try the following in your vimrc:
inoremap <TAB> <space><space><space><space>
That will make vim insert 4 spaces.

I need a text editor that jumps to a column with no character or whitespace

General text editors can jump to a specific column number of a file by either using movement keybindings or mouse click.
This can only happen if there is already character or whitespace on that column, otherwise the cursor will be lock to the last column which contains that character or whitespace.
For example, in the following line:
1 2 3 4 5
012345678901234567890123456789012345678901234567890
The quick brown fox jumps over the lazy dog
I want to jump to column 35, just before the word lazy, I can click on the mouse to that column or use 8 words movement to get to that position.
If I want to jump to column 50 I can only get to column 43 because that is the newline character and there are no more characters after that.
Now I remember I used a text editor that I could click on the column 50 and automatically will insert the whitespaces and move the newline character to that column, but could not remember which text editor was.
So, I am wandering which text editor is able to do that, or a script for vim that can actually achieve the same effect.
Thank you
:set virtualedit=all
Afterwards 50| will jump to the 50th column, even when the actual line is shorter than that.
More information: :help 'virtualedit'

How can I insert a real tab character in Vim?

When I have my vimrc here:
set tabstop=2
set shiftwidth=2
set softtabstop=2
set expandtab
set smarttab
And I have supertab plugin installed. Whenever I am in insert mode I press tab, it shows the auto completion, but sometimes I would like to insert a real tab character in a string literal like. So what I mean whenever I press tab in double quotes string literal, it should input we a real tab character.
While in insert mode or command mode (the : prompt at the bottom of the editor), type CTRL + V then TAB.
Using CTRL + V signals Vim that it should take the next character literally. Even in insert mode.
UPDATE:
As noted by Herbert Sitz, if gVim is in Windows mode (default), you must use CRTL + Q in place of CTRL + V.
#Samnang: I have a similar setup as you; unfortunately, Jason's answer did not work, for me.
This is a workaround:
Substitute some character (e.g. a backtick: `) or characters (e.g. a unique alphanumeric string: zzz) where you want your tab(s)
Select the text (Visual mode) and do a search/replace,
:'s/`/\t/g
Updated answer, inspired by #Cyryl1972 's comment.
To insert a tab at beginning of all lines (note also: no need to select lines, for any of the following code, as that's included in the line matching part of the expression):
:1,$s/^/\t\1/
Insert tab after first 10 characters in all lines:
:1,$s/^\(.\{10}\)/\1\t/
Explanation - first part:
:1,$ Match from line 1 to end of file
^(.{10} Collect (preserve) all text from beginning of line to position 10
(you need to escape the parentheses, \( and \), as well the FIRST
(left) curly brace, only: \{ -- as it, { , appears to have special
meaning in regex when used for this purpose
Explanation - second part:
/1 Add back the preserved text
\t Insert a tab
... and the rest of the line is automatically restored, as well.
Current line, only:
:s/^/\t\1/
Example: insert tab at position 10 (0-indexed) at lines 2-4:
1234567890abcdefghij
1234567890abcdefghij
1234567890abcdefghij
1234567890abcdefghij
1234567890abcdefghij
:2,4s/^\(.\{10}\)/\1\t/
1234567890abcdefghij
1234567890 abcdefghij
1234567890 abcdefghij
1234567890 abcdefghij
1234567890abcdefghij
References (StackOverflow):
Replace character in specific position with another character using regular expression, Vim
How do I add a character at a specific position in a string?
References (other):
Regex Cheat Sheet

How to fill a line with character x up to column y using Vim

How can I fill the remainder of a line with the specified character up to a certain column using Vim? For example, imagine that the cursor is on column four and I want to fill the remainder of the current line with dashes up to column 80. How would I do that?
You can do 80Ax<Esc>d80| for a simpler solution.
Here's a function to implement what you ask, and slightly more.
It fills the line from its current end of line, rather than the cursor position
It forces a single space between what's currently on the line and the repeated chars
It allows you to specify any string to fill the rest of the line with
It uses vim's textwidth setting to decide how long the line should be
(rather than just assuming 80 chars)
The function is defined as follows:
" fill rest of line with characters
function! FillLine( str )
" set tw to the desired total length
let tw = &textwidth
if tw==0 | let tw = 80 | endif
" strip trailing spaces first
.s/[[:space:]]*$//
" calculate total number of 'str's to insert
let reps = (tw - col("$")) / len(a:str)
" insert them, if there's room, removing trailing spaces (though forcing
" there to be one)
if reps > 0
.s/$/\=(' '.repeat(a:str, reps))/
endif
endfunction
Insert that into your .vimrc, and make a mapping to it, e.g.
map <F12> :call FillLine( '-' )
Then you can press F12 to apply the hyphens to the current line
Note: this could probably be easily extended to act on a selection in VISUAL mode, but currently works for single lines only.*
If I understand the question correctly, this can be accomplished like this: in normal mode subtract the cursor's current column position from the desired ending column, then type the result followed by 'i' to enter insert mode, then the character you want to fill the space with. End by returning to normal mode. For example, with the cursor at column four in normal mode, if you wanted to fill the rest of the line up to column 80 with dashes, type 76i- then Esc or Ctrl-[ to return to normal mode. This should result in 76 dashes starting in column 4 and ending in column 79.
If you have the virtualedit option set to block or all, you can create a visual selection (even over empty space) up to the desired column:
v80| (if virtualedit=all) or
<c-v>80| (if virtualedit=block)
Then replace the selected area with dashes:
r-
It's probably helpful to start visual mode after the last character in the line by hitting l to avoid overwriting the last character in the line. If you are not using virtualedit=all, then you need to set virtualedit+=onemore so you can move one character beyond the end of line in normal mode.
One of the other answers here is: 80Ax<Esc>d80|. I initially started using it as a key mapping like this:
nnoremap <leader>- 80A-<Esc>d80<bar>
...but I didn't like how it leaves the cursor at the end of the line. Also, for narrow windows (e.g. just a little wider than 80 cells), it causes the entire window to scroll horizontally because the cursor briefly jumps to the end of the long line before it's trimmed back to 80. This is partially resolved by returning to the beginning of the line:
nnoremap <leader>- 80A-<Esc>d80<bar>0
...but then the screen will "flash" briefly while the cursor jumps offscreen and back (thus scrolling the window briefly to the right and back). To prevent this, we can temporarily use reverse insert mode (:h revins or :h ri) to keep the cursor onscreen while appending. Here's the full command as a key mapping:
nnoremap <leader>- :set ri<cr>80A-<esc>81<bar>d$0:set nori<cr>
This answer answers your question. Just replace len computation with your desired column number (+/- 1 may be, I never remember), and remove the enclosing double-quotes added by the substitution.
Using the textwidth value is also possible. It allows for different maximum line widths depending on filetype (check :h 'tw'). Here is what I now use, it appends a space after existing line content if present and will prompt for the string to use for the pattern:
function! FillLine() abort
if &textwidth
let l:str = input('FillLine>')
.s/\m\(\S\+\)$/\1 /e " Add space after content (if present).
" Calculate how many repetitions will fit.
let l:lastcol = col('$')-1 " See :h col().
if l:lastcol > 1
let l:numstr = float2nr(floor((&textwidth-l:lastcol)/len(l:str)))
else
let l:numstr = float2nr(floor(&textwidth/len(l:str)))
endif
if l:numstr > 0
.s/\m$/\=(repeat(l:str, l:numstr))/ " Append repeated pattern.
endif
else
echohl WarningMsg
echom "FillLine requires nonzero textwidth setting"
echohl None
endif
endfunction
You can map it for quick access of course. I like:
nnoremap <Leader>' :call FillLine()<Cr>
Note that the calculation assumes simple ASCII characters are being inserted. For more complicated strings, len(l:str) might not work. From :h strlen():
If you want to count the number of multi-byte characters use strchars().
Also see len(), strdisplaywidth() and strwidth().
You can insert first your dashes and then go to the first character and enter your text in replace mode: 80i-Esc0R
if you don't want to type the text, first delete the line with 0D, use 80i-Esc to insert the dashes and 0RCTRL+r " to paste the contents of the unamed register in replace mode.
I actually stumbled across this looking to align columns. Just in case anyone else does the same, this thread might be useful: How to insert spaces up to column X to line up things in columns?

Resources