TransWikia.com

How can I indent selected lines in Visual mode by space granularity instead of TABs?

Vi and Vim Asked on November 22, 2021

How can I indent lines selected in Visual mode using space granularity instead of than TABs (i.e. < or >)?

Sometimes when coding Python, I need to indent lines to a level that is not a TAB boundary.

Selecting the lines by pressing V to enter Visual mode and then using < or > to indent them doesn’t let me indent the lines to the anticipated level.

Any ideas?

Update:

Suppose after some edits, the dots (‘.‘) are not aligned, which they must be in order to satisfy the Flake8 Python linter:

    # Flake8: All dots ('.') should be aligned over each other
    proj_object = self.session.query(Project) 
                            .filter(Project.id == parsed_proj.id) 
                            .one_or_none()

In particular, all dots must have the same indentation as the topmost dot, which is not on a TAB boundary. It’s very cumbersome to manually ident each line individually using SPACEs… I’d like to indent all lines in Visual mode at once using SPACEs (not TABs):

    # Flake8: No complains
    proj_object = self.session.query(Project) 
                              .filter(Project.id == parsed_proj.id) 
                              .one_or_none()

NB: I have looked at expandtab, but that doesn’t really suit my needs.

3 Answers

Three steps to success:

  1. Ctrl + V and select the lines you want to add indentation to using arrow keys,
  2. Shift + i and add the number of spaces require to align the first line,
  3. Hit ESC and watch the magic happen!!!

Answered by Canute S on November 22, 2021

I just do column selection + copy for such unaligned movement of my code.

So... first I have expandtab (et) as part of my settings at the bottom of my file like so:

// vim: ts=4 sw=4 et

This means the tabstop is at 4 characters, the shiftwidth is also at 4 characters, and expandtab is also turned on (so no tabs anywhere, just spaces).

When I have a problem like yours above, so this code:

proj_object = self.session.query(Project) 
                        .filter(Project.id == parsed_proj.id) 
                        .one_or_none()

I go under the first line and select using Ctrl-V (column select), in this case go down once, do y to yank that column and then p to copy the column. I repeat the p until the alignment is correct (twice in your example).

Here is a screenshot showing the selection of the column:

enter image description here

As long as all the characters before the . are spaces, the Ctrl-V can happen at any location.

Note: when the selection is rather large, I use my mouse. Although it's possible to enter the column selection with the mouse, it require a quadruple click which I find annoying. However, with just one left click and selection, then Ctrl-V problem solved! You can actually use v and Ctrl-V any number of times to switch between the two selection modes (and V too [capital], which is used to select whole lines).

Answered by Alexis Wilke on November 22, 2021

A working, hacky solution

function! Visual_indent_with_space() range abort
  '<,'>g/./exe "normal! " v:count1 . "I "
endfunction
vnoremap <leader><space> :call Visual_indent_with_space()<cr>

Breaking down: '<,'>g/./exe "normal! " v:count1 . "I "

  • '<,'>: use the selected range
  • g/./: apply a global command to each selected line (. matches each line)
  • exe "normal! " execute a normal command
  • v:count1 . "I " insert v:count1 spaces at the beginning of these lines

From the :h v:count and :h v:count1`:

v:count     The count given for the last Normal mode command.  Can be used
            to get the count before a mapping.  Read-only.
v:count1    Just like "v:count", but defaults to one when no count is
            used.

Also see:

  • :h :global
  • :h :range
  • :h :execute

An elegant solution that does not work as expected

This would be an elegant solution, but it only works on the first line.

Type [N]<leader><space> to insert [N] spaces at the beginning of selected lines.

vnoremap <leader><space> @='I <C-V><Esc>'<CR>
  • @: execute the content of the register
  • =: use the expression register
  • '': boundaries of the content to load in the expression register
  • I : insert a space at the start of the line
  • <c-v><esc>: input a <escape character (<c-v> escapes the character)
  • <cr>: validate the command

Not an elegant solution

With this, you can select your lines and hit the mapping to insert a space at the beginning of the selected lines:

vnoremap <leader><space> :norm I<space><cr>gv
  • :norm: start a normal command (i.e. like in normal mode) from command-line
  • I: start inserting at the beginning of the line.
  • <space>: insert a space
  • <cr>: validate the command
  • gv: go back to visual mode (so you can repeat the process)

If your leader is space, you can mash space to insert as many as you want.

If it is not, you could map with <space><space>:

vnoremap <space><space> :norm I<space><cr>gv

Answered by Biggybi on November 22, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP