Introduction

Vim's visual block mode (Ctrl+V) allows column-based text selection and editing. When files contain tabs, the visual block selection does not align with the visual column positions because tabs display as multiple screen columns but count as a single character:

``` File content (tabs shown as →): →→var x = 1; →var y = 2; var z = 3;

Visual block selection starts at wrong column because tab width differs from character count ```

Symptoms

  • Block selection starts at unexpected column positions
  • Inserting text in block mode shifts lines unevenly
  • Block selection spans different columns on different lines
  • Deleting a block of text produces misaligned results
  • Works correctly on space-indented files but breaks on tab-indented files

Common Causes

  • Mixed tabs and spaces in the file
  • tabstop setting differs from the file's actual tab width
  • virtualedit not enabled, preventing cursor placement in virtual columns
  • File uses hard tabs but expandtab is set in Vim
  • Screen column position differs from character position due to tab expansion

Step-by-Step Fix

  1. 1.Enable virtualedit for precise block mode positioning:
  2. 2.```vim
  3. 3." Allow cursor to move to any screen column
  4. 4.set virtualedit=block
  5. 5.`
  6. 6.This lets you position the block cursor at any visual column, even within tab characters.
  7. 7.Verify tab settings match the file:
  8. 8.```vim
  9. 9.:set tabstop? softtabstop? shiftwidth?
  10. 10.`
  11. 11.Ensure these match the file's indentation:
  12. 12.```vim
  13. 13.set tabstop=4
  14. 14.set softtabstop=4
  15. 15.set shiftwidth=4
  16. 16.`
  17. 17.Convert tabs to spaces for consistent block editing:
  18. 18.```vim
  19. 19." Convert all tabs to spaces in the current file
  20. 20.:set expandtab
  21. 21.:retab
  22. 22.`
  23. 23.This makes block selection align correctly because every character occupies exactly one column.
  24. 24.Detect and fix mixed indentation:
  25. 25.```vim
  26. 26." Show tabs as visible characters
  27. 27.:set list listchars=tab:»·,trail:·

" Find lines with mixed tabs and spaces /^\t* \+/ ```

  1. 1.Use block mode with virtualedit for precise editing:
  2. 2.`
  3. 3.1. Ctrl+V to start visual block mode
  4. 4.2. Move cursor to select the desired block
  5. 5.3. I (uppercase) to insert text at the start of the block
  6. 6.4. Type the text
  7. 7.5. Esc to apply to all lines
  8. 8.`

Prevention

  • Use set expandtab in your .vimrc to convert tabs to spaces automatically
  • Configure EditorConfig (.editorconfig) for consistent indentation across the team
  • Use set list to visualize tabs and trailing whitespace
  • Run :retab after changing tab settings to normalize existing files
  • Add a pre-commit hook to check for mixed indentation
  • Use :set virtualedit=block as a standard setting for developers who use block mode
  • For existing projects with tab indentation, match the project's tabstop setting
  • Document the team's indentation standard (spaces vs tabs, width) in CONTRIBUTING.md