Vim Tips Wiki
m (Reverted edits by 58.194.224.124 (talk | block) to last version by JohnBot)
 
(7 intermediate revisions by 5 users not shown)
Line 3: Line 3:
 
|previous=1591
 
|previous=1591
 
|next=1593
 
|next=1593
|created=February 16, 2008
+
|created=2008
 
|complexity=basic
 
|complexity=basic
 
|author=Metacosm
 
|author=Metacosm
Line 11: Line 11:
 
|category2=
 
|category2=
 
}}
 
}}
You can set the '<tt>expandtab</tt>' (abbreviated to '<tt>et</tt>') option so each tab that you type is converted to an equivalent number of spaces. And you can use the <tt>:retab</tt> command to convert all existing tabs to spaces. You can do both in one command:
+
This tip explains how <code>:retab</code> converts tabs to spaces, or spaces to tabs, and provides a "super retab" command to convert only the whitespace used for indentation in programs.
  +
  +
==Standard retab==
  +
You can set the '<code>expandtab</code>' (abbreviated to '<code>et</code>') option so each tab that you type is converted to an equivalent number of spaces. And you can use the <code>:retab</code> command to convert all existing tabs to spaces. You can do both in one command:
 
<pre>
 
<pre>
 
:set et|retab
 
:set et|retab
Line 26: Line 29:
   
 
==Super retab==
 
==Super retab==
Use the following command to define a new <tt>SuperRetab</tt> command. You could enter this in Vim, or put it in your [[vimrc]]:
+
Use the following command to define a new <code>SuperRetab</code> command. You could enter this in Vim, or put it in your [[vimrc]]:
 
<pre>
 
<pre>
 
:command! -nargs=1 -range SuperRetab <line1>,<line2>s/\v%(^ *)@<= {<args>}/\t/g
 
:command! -nargs=1 -range SuperRetab <line1>,<line2>s/\v%(^ *)@<= {<args>}/\t/g
 
</pre>
 
</pre>
   
For example, you may have a code snippet which uses two-space indents, and you want to entab the indents (convert each leading group of two spaces to a tab). To do this, visually select the code (press <tt>V</tt> then <tt>j</tt>), then enter:
+
For example, you may have a code snippet which uses two-space indents, and you want to entab the indents (convert each leading group of two spaces to a tab). To do this, visually select the code (press <code>V</code> then <code>j</code>), then enter:
 
<pre>
 
<pre>
 
:'<,'>SuperRetab 2
 
:'<,'>SuperRetab 2
Line 43: Line 46:
 
</pre>
 
</pre>
   
to the following ("<tt>|-------</tt>" represents a tab):
+
to the following ("<code>|-------</code>" represents a tab):
 
<pre>
 
<pre>
 
|-------for {
 
|-------for {
Line 50: Line 53:
 
</pre>
 
</pre>
   
The command <tt>:SuperRetab 5</tt> would give the same result from the following selected text:
+
The command <code>:SuperRetab 5</code> would give the same result from the following selected text:
 
<pre>
 
<pre>
 
for {
 
for {
Line 60: Line 63:
 
An alternative super retab procedure is to use the following two commands:
 
An alternative super retab procedure is to use the following two commands:
 
<pre>
 
<pre>
:command! -range=% -nargs=0 Tab2Space execute "<line1>,<line2>s/^\\t\\+/\\=substitute(submatch(0), '\\t', repeat(' ', ".&ts."), 'g')"
+
:command! -range=% -nargs=0 Tab2Space execute '<line1>,<line2>s#^\t\+#\=repeat(" ", len(submatch(0))*' . &ts . ')'
:command! -range=% -nargs=0 Space2Tab execute "<line1>,<line2>s/^\\( \\{".&ts."\\}\\)\\+/\\=substitute(submatch(0), ' \\{".&ts."\\}', '\\t', 'g')"
+
:command! -range=% -nargs=0 Space2Tab execute '<line1>,<line2>s#^\( \{'.&ts.'\}\)\+#\=repeat("\t", len(submatch(0))/' . &ts . ')'
 
</pre>
 
</pre>
   
The above defines a <tt>Tab2Space</tt> and a <tt>Space2Tab</tt> command that convert leading whitespace (spaces and tabs that are not at the beginning of a line are not affected). These commands use the current '<tt>tabstop</tt>' (abbreviated as '<tt>ts</tt>') option.
+
The above defines a <code>Tab2Space</code> and a <code>Space2Tab</code> command that convert leading whitespace (spaces and tabs that are not at the beginning of a line are not affected). These commands use the current '<code>tabstop</code>' (abbreviated as '<code>ts</code>') option.
   
 
Examples:
 
Examples:
Line 78: Line 81:
 
</pre>
 
</pre>
   
==Comments==
+
==Script==
  +
A more elaborate solution is to use the following script which provides these features:
  +
*The commands allow an argument to specify the column width; if none is given, the <code>'tabstop'</code> setting is used.
  +
*Redundant spaces in an indent are removed (in the above mapping, converting tabs to spaces will not change lines where there is a space before a tab in the indent).
  +
*The search history is not changed (pressing <code>n</code> will do the same search it would have done before the conversion was performed).
  +
*The cursor position is restored, although the column will be slightly wrong owing to the different number of characters in the indent.
   
  +
These commands are provided:
I‘m using Python and have to switch from Tabs to spaces while loading and vice-versa while saving, as the project prefers tabs for some reason, but I want to edit the files using spaces.
 
  +
{| class="cleartable"
 
  +
| <code>Space2Tab</code> || Convert spaces to tabs, only in indents.
It‘s easy to convert all tabs to spaces, but vice-versa it‘s a hard job. Searching for sequences of 4 left-margin-spaces and replacing them with tabs does not work, as it might convert too many spaces. Example: (-\t- is a tab)
 
  +
|-
  +
| <code>Tab2Space</code> || Convert tabs to spaces, only in indents.
  +
|-
  +
| <code>RetabIndent</code> || Execute <code>Space2Tab</code> (if <code>'expandtab'</code> is set), or <code>Tab2Space</code> (otherwise).
  +
|}
   
  +
Each command accepts an argument that specifies the number of spaces in a tab column. By default, the <code>'tabstop'</code> setting is used.
 
<pre>
 
<pre>
  +
" Return indent (all whitespace at start of a line), converted from
def foobar():
 
  +
" tabs to spaces if what = 1, or from spaces to tabs otherwise.
mylist = ['el1',
 
  +
" When converting to tabs, result has no redundant spaces.
'el2',
 
  +
function! Indenting(indent, what, cols)
]
 
  +
let spccol = repeat(' ', a:cols)
print "Foo"
 
  +
let result = substitute(a:indent, spccol, '\t', 'g')
</pre>
 
  +
let result = substitute(result, ' \+\ze\t', '', 'g')
  +
if a:what == 1
  +
let result = substitute(result, '\t', spccol, 'g')
  +
endif
  +
return result
  +
endfunction
   
  +
" Convert whitespace used for indenting (before first non-whitespace).
Using retab or something similar I would get the following:
 
  +
" what = 0 (convert spaces to tabs), or 1 (convert tabs to spaces).
 
  +
" cols = string with number of columns per tab, or empty to use 'tabstop'.
<pre>
 
  +
" The cursor position is restored, but the cursor will be in a different
def foobar():
 
  +
" column when the number of characters in the indent of the line is changed.
-\t-mylist = ['el1',
 
  +
function! IndentConvert(line1, line2, what, cols)
-\t--\t--\t- 'el2',
 
  +
let savepos = getpos('.')
-\t--\t--\t- ]
 
  +
let cols = empty(a:cols) ? &tabstop : a:cols
print "Foo"
 
  +
execute a:line1 . ',' . a:line2 . 's/^\s\+/\=Indenting(submatch(0), a:what, cols)/e'
 
  +
call histdel('search', -1)
But I want this one:
 
  +
call setpos('.', savepos)
def foobar():
 
  +
endfunction
-\t-mylist = ['el1',
 
  +
command! -nargs=? -range=% Space2Tab call IndentConvert(<line1>,<line2>,0,<q-args>)
-\t- 'el2',
 
  +
command! -nargs=? -range=% Tab2Space call IndentConvert(<line1>,<line2>,1,<q-args>)
-\t- ]
 
  +
command! -nargs=? -range=% RetabIndent call IndentConvert(<line1>,<line2>,&et,<q-args>)
print "Foo"
 
 
</pre>
 
</pre>
   
  +
==See also==
So… Python has indent levels, if a line ends with a ':' char (outside of comments of course), the indent level raises by one and the following lines have a new tab. If there is another ':$', the following lines start with two tabs etc. These spaces should be converted to tabs, but no more.
 
  +
*[[Converting tabs to spaces]]
  +
*[[Highlight unwanted spaces]]
  +
*[[Indent with tabs, align with spaces]]
  +
*[[Indenting source code]]
   
  +
==Comments==
An indent level decreases, if there is not enough space, ie. there are not enough spaces to be converted into tabs. In the example above the indent level was zero at the beginning, than increases to one because of the ':' and decreases in the last line because of a lack of leading spaces.
 
 
I hope you guys can help me here, I don’t have much experience with VimL and am not able to do it myself, but I suggest some iterative function could work here.
 
 
Thanks, Keba.
 
 
----
 
 
First, beyond being a great self-help resource, in general this wiki is not a good place for getting help with a problem using Vim. There are just not enough eyes to see your question:
 
 
 
:We don't encourage questions about using Vim on the wiki because they distract from the work of improving the tip collection. Of course, feel free to comment if you have questions about or see deficiencies in a tip, but please see [[Vim_Tips_Wiki:Community_Portal#Asking_questions|'''how to ask questions''']] for issues regarding Vim itself. <small>This&nbsp;comment was added on 2011-07-04 and will be removed after a week.</small>[[Category:Temporary]]
 
 
But I'm going to take a quick stab and ask the obvious:
 
 
Why exactly do you feel the need to edit using spaces for indent, to the extent that you want to create complex solutions for automatically switching back and forth between the two? Vim is made so that you can very easily work with either, using all the same commands, for no difference in effort or experience either way. Set your tabstop &ndash; and possibly shiftwidth, softtabstop, and smarttab &ndash; settings as you want, and just edit as normal. One of the reasons often given for using tabs for indent, is that different people can set different tabstop values and edit using their own preferred indent. Use it!
 
 
For the record, I slightly prefer spaces for indent myself, but honestly I'd rather set Vim up to use and work with the established project practice, than try to come up with a complicated solution to work around the established practices.
 
 
--[[User:Fritzophrenic|Fritzophrenic]] 03:47, July 4, 2011 (UTC)
 

Latest revision as of 12:30, 2 September 2012

Tip 1592 Printable Monobook Previous Next

created 2008 · complexity basic · author Metacosm · version 7.0


This tip explains how :retab converts tabs to spaces, or spaces to tabs, and provides a "super retab" command to convert only the whitespace used for indentation in programs.

Standard retab[]

You can set the 'expandtab' (abbreviated to 'et') option so each tab that you type is converted to an equivalent number of spaces. And you can use the :retab command to convert all existing tabs to spaces. You can do both in one command:

:set et|retab

You can also convert spaces to tabs:

:set noet|retab!

Both of the above examples should be used with caution. They convert all sequences, even those that might be in a "quoted string like this".

This tip shows how to convert only the indents at the left margin. Any spaces or tabs after the first non-white character are not affected.

Super retab[]

Use the following command to define a new SuperRetab command. You could enter this in Vim, or put it in your vimrc:

:command! -nargs=1 -range SuperRetab <line1>,<line2>s/\v%(^ *)@<= {<args>}/\t/g

For example, you may have a code snippet which uses two-space indents, and you want to entab the indents (convert each leading group of two spaces to a tab). To do this, visually select the code (press V then j), then enter:

:'<,'>SuperRetab 2

The above command would change:

  for {
    that;
  }

to the following ("|-------" represents a tab):

|-------for {
|-------|-------that;
|-------}

The command :SuperRetab 5 would give the same result from the following selected text:

     for {
          that;
     }

Alternative[]

An alternative super retab procedure is to use the following two commands:

:command! -range=% -nargs=0 Tab2Space execute '<line1>,<line2>s#^\t\+#\=repeat(" ", len(submatch(0))*' . &ts . ')'
:command! -range=% -nargs=0 Space2Tab execute '<line1>,<line2>s#^\( \{'.&ts.'\}\)\+#\=repeat("\t", len(submatch(0))/' . &ts . ')'

The above defines a Tab2Space and a Space2Tab command that convert leading whitespace (spaces and tabs that are not at the beginning of a line are not affected). These commands use the current 'tabstop' (abbreviated as 'ts') option.

Examples:

" Convert all leading spaces to tabs (default range is whole file):
:Space2Tab
" Convert lines 11 to 15 only (inclusive):
:11,15Space2Tab
" Convert last visually-selected lines:
:'<,'>Space2Tab
" Same, converting leading tabs to spaces:
:'<,'>Tab2Space

Script[]

A more elaborate solution is to use the following script which provides these features:

  • The commands allow an argument to specify the column width; if none is given, the 'tabstop' setting is used.
  • Redundant spaces in an indent are removed (in the above mapping, converting tabs to spaces will not change lines where there is a space before a tab in the indent).
  • The search history is not changed (pressing n will do the same search it would have done before the conversion was performed).
  • The cursor position is restored, although the column will be slightly wrong owing to the different number of characters in the indent.

These commands are provided:

Space2Tab Convert spaces to tabs, only in indents.
Tab2Space Convert tabs to spaces, only in indents.
RetabIndent Execute Space2Tab (if 'expandtab' is set), or Tab2Space (otherwise).

Each command accepts an argument that specifies the number of spaces in a tab column. By default, the 'tabstop' setting is used.

" Return indent (all whitespace at start of a line), converted from
" tabs to spaces if what = 1, or from spaces to tabs otherwise.
" When converting to tabs, result has no redundant spaces.
function! Indenting(indent, what, cols)
  let spccol = repeat(' ', a:cols)
  let result = substitute(a:indent, spccol, '\t', 'g')
  let result = substitute(result, ' \+\ze\t', '', 'g')
  if a:what == 1
    let result = substitute(result, '\t', spccol, 'g')
  endif
  return result
endfunction

" Convert whitespace used for indenting (before first non-whitespace).
" what = 0 (convert spaces to tabs), or 1 (convert tabs to spaces).
" cols = string with number of columns per tab, or empty to use 'tabstop'.
" The cursor position is restored, but the cursor will be in a different
" column when the number of characters in the indent of the line is changed.
function! IndentConvert(line1, line2, what, cols)
  let savepos = getpos('.')
  let cols = empty(a:cols) ? &tabstop : a:cols
  execute a:line1 . ',' . a:line2 . 's/^\s\+/\=Indenting(submatch(0), a:what, cols)/e'
  call histdel('search', -1)
  call setpos('.', savepos)
endfunction
command! -nargs=? -range=% Space2Tab call IndentConvert(<line1>,<line2>,0,<q-args>)
command! -nargs=? -range=% Tab2Space call IndentConvert(<line1>,<line2>,1,<q-args>)
command! -nargs=? -range=% RetabIndent call IndentConvert(<line1>,<line2>,&et,<q-args>)

See also[]

Comments[]