How Do Filetype Plugins Interact With Vim Auto-Indent?
2025-09-04 22:00:34
180
4 Answers
Nora
2025-09-05 01:34:11
I get picky about editor behavior, so I like to think in terms of responsibilities: ftplugins are for configuration and user conveniences, indent scripts are for the algorithm. The key detail is that both operate per-buffer, so they communicate by reading and writing buffer-local options. Order matters because buffer-local options don't merge — they get replaced. That’s why you’ll often see an ftplugin set shiftwidth and an indent/.vim set indentexpr; if indentexpr is present, Vim uses that function to compute indentation regardless of simpler flags like 'smartindent'.
A few nuanced rules I rely on: FileType autocommands trigger ftplugin loading (which commonly runs first), but distributions and plugins can change load order; indent scripts often guard themselves with b:did_indent to avoid double-loading, so you can set that flag manually in an ftplugin to prevent an indent script from running. For persistent tweaks I use after/ftplugin/.vim or after/indent/.vim so my preferences load after built-in scripts. And when debugging, :setlocal, :verbose setlocal, and :scriptnames are my friends — they tell me who stomped on what. In short, treat ftplugin for the style and indent/ for the brain, and use 'after' or b:did_indent when you want to pick the winner.
Caleb
2025-09-09 17:49:50
If you've ever opened a file in Vim and wondered why indentation behaves one way in one project and differently in another, the way filetype plugins and indent scripts interact is the usual culprit. In my messy but beloved setup I keep separate snippets in ~/.vim/ftplugin/ and ~/.vim/indent/ and they each have a job: ftplugin files generally set buffer-local editing options (things like shiftwidth, tabstop, expandtab, mappings) while indent scripts (under indent/) provide indentation logic by setting 'indentexpr', 'cindent', 'indentkeys', or related buffer-local options. Because these are buffer-local, whichever script writes a particular option last wins for that buffer.
Practically that means you can get conflicts. An ftplugin might set 'shiftwidth' to 4 for 'python' and an indent script might expect 2; or an indent script will set 'indentexpr' to a custom function that overrides simpler behaviors such as 'autoindent'. The usual fixes I use are: enable both with :filetype plugin indent on, then put overrides in after/ftplugin/ or after/indent/ so they load later; or explicitly set local options with setlocal in a ftplugin; or prevent an indent script with let b:did_indent = 1 if you deliberately want to skip it. For debugging, :scriptnames shows what got sourced, and :verbose setlocal shiftwidth? / :verbose setlocal indentexpr? tell you who last changed a setting. I like keeping ftplugin for styling and small mappings, and leaving indentation math to indent scripts — but I always keep an 'after' copy for those moments when I need the last word.
Wyatt
2025-09-09 18:30:33
I usually keep things simple: filetype plugins and indent scripts both run when filetype detection is enabled, but they have different jobs. The ftplugin configures buffer-local settings and convenience stuff, and the indent script provides the indentation logic (often by setting 'indentexpr' or enabling 'cindent'). Because these scripts set buffer-local options, whichever one runs later or explicitly sets an option last determines the final behavior in that buffer.
If you need to override built-in behavior, put changes in after/ftplugin/ or after/indent/ so they load after the defaults, or disable indent scripts with :filetype indent off. For quick fixes, :verbose setlocal shiftwidth? and :scriptnames show recent changes and loaded scripts. It’s worked for me whenever a project insists on tabs while my brain prefers spaces.
Nevaeh
2025-09-09 23:39:40
I tend to tinker a lot, so here's the practical short guide I use every time Vim mis-indents: Vim treats filetype plugins (ftplugin/.vim) and indent scripts (indent/.vim) as separate things. The ftplugin usually configures buffer-specific options like tabstop or shiftwidth and adds convenient mappings, while the indent script defines the actual indentation logic by setting 'indentexpr', 'cindent', or 'indentkeys'. If both try to control the same option, the last file that sets it wins for that buffer.
To control the order, you can place custom changes in after/ftplugin/ or after/indent/ so they load later, or set let b:did_indent = 1 inside an ftplugin to skip the indent script. If you want to turn off indent scripts entirely, use :filetype indent off. When things go weird I use :scriptnames to see what loaded and :verbose setlocal indentexpr? to find who changed it. That little workflow saves me from pointless reformatting fights.
Grace Anderson is a striking young lady with a no-nonsense and inimical attitude. She barely smiles or laughs, the feeling of pure happiness has been rare to her. She has acquired so many scars and life has thought her a very valuable lesson about trust.
Dean Ryan is a good looking young man with a sanguine personality. He always has a smile on his face and never fails to spread his cheerful spirit.
On Grace's first day of college, the two meet in an unusual way when Dean almost runs her over with his car in front of an ice cream stand. Although the two are opposites, a friendship forms between them and as time passes by and they begin to learn a lot about each other, Grace finds herself indeed trusting him.
Dean was in love with her. He loved everything about her.
Every. Single. Flaw.
He loved the way she always bit her lip.
He loved the way his name rolled out of her mouth.
He loved the way her hand fit in his like they were made for each other.
He loved how much she loved ice cream.
He loved how passionate she was about poetry.
One could say he was obsessed.
But love has to have a little bit of obsession to it, right?
It wasn't all smiles and roses with both of them but the love they had for one another was reason enough to see past anything.
But as every love story has a beginning, so it does an ending.
Have you ever wondered how to mate with an Alpha?
Have you ever wondered how to capture the heart of the most powerful man in the land and have him completely in your grasp? Well, I did.
***********
The fool clenched his fists by his sides. “The fact that you were born an omega made things terrible for you and now that you made the wise decision to become the famous prostitute of the town you’re even more disgusting to me. Now you can get over whatever fucked up and deluded version you had of us in your head.”
“I, Beta Meidran Hall of the Etrana Pack, reject you, Samiya Cordova, as my mate and I hereby break any bond we might share.”
***********
Samiya Cordova, a lowly omega, and popular pack slut finds her entire life come crumbling down when she gets rejected by the Beta Meidran. Heart broken, torn, and slightly vengeful, she makes a vow to do anything she can in her power to steal the heart of the Alpha in order to get her ultimate revenge.
Machines of Iron and guns of alchemy rule the battlefields. While a world faces the consequences of a Steam empire.
Molag Broner, is a soldier of Remas. A member of the fabled Legion, he and his brothers have long served loyal Legionnaires in battle with the Persian Empire. For 300 years, Remas and Persia have been locked in an Eternal War. But that is about to end.
Unbeknown to Molag and his brothers. Dark forces intend to reignite a new war. Throwing Rome and her Legions, into a new conflict
Is it LOVE?
Really?
~~~~~~~~~~~~~~~~~~~~~~~~
Two brothers separated by fate, and now fate brought them back together.
What will happen to them?
How do they unlock the questions behind their separation?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"There Are THREE SIDES To Every Story. YOURS, HIS And The TRUTH."We both hold distaste for the other. We're both clouded by their own selfish nature. We're both playing the blame game. It won't end until someone admits defeat. Until someone decides to call it quits. But how would that ever happen? We're are just as stubborn as one another.Only one thing would change our resolution to one another. An Engagement. .......An excerpt -" To be honest I have no interest in you. ", he said coldly almost matching the demeanor I had for him, he still had a long way to go through before he could be on par with my hatred for him. He slid over to me a hot cup of coffee, it shook a little causing drops to land on the counter. I sighed, just the sight of it reminded me of the terrible banging in my head. Hangovers were the worst. We sat side by side in the kitchen, disinterest, and distaste for one another high. I could bet if it was a smell, it'd be pungent."I feel the same way. " I replied monotonously taking a sip of the hot liquid, feeling it burn my throat. I glanced his way, staring at his brown hair ruffled, at his dark captivating green eyes. I placed a hand on my lips remembering the intense scene that occurred last night. I swallowed hard. How? I thought. How could I be interested?I was in love with his brother.
A strong, influential, and well-respected man in society, has a high sexual libido, hence, sex became his weak point. He was carried away by the touch of young women till he got lots.
His pretty wife, after years of being angry with his high sexual habit resolved to save him with the same thing he loved most, which is also his weak point- sex.
Later, she realized that she did not just save her home and marriage, but also her husband's life, call, and career.
If you're fiddling with Vim's indentation and want precise control, the trio I reach for is :set shiftwidth, :set tabstop, and :set softtabstop.
shiftwidth (sw) controls how many spaces a single indentation level uses for operations like >>, <<, and automatic indentation. I usually do :setlocal shiftwidth=4 for projects that use four-space indents. tabstop (ts) sets how many spaces a literal TAB character displays as; use :set tabstop=4 to make existing tabs line up visually with your intended width. softtabstop (sts) affects insert-mode behavior: :set softtabstop=4 makes pressing Backspace or Tab behave like you're working with 4-space logical tabs even if actual file uses tabs.
A couple of other practical commands I keep in my .vimrc: :set expandtab to insert spaces instead of real tabs (or :set noexpandtab to keep tabs), :set autoindent to keep the previous line's indentation, and :set cindent or :set smartindent for C-like auto-indenting. If you want the changes to apply only to the current buffer, use :setlocal sw=2 ts=2 sts=2. To reformat an entire file after changing settings, I often run gg=G to reindent the whole buffer, or :retab to convert tabs to spaces (or the reverse with :retab!). These little tweaks saved me hours when I was switching between Python, Makefiles, and Go projects.
Okay, here's a practical and friendly way I handle Vim's auto-indent when I need it out of the way for a few moments.
If I just want to paste something without Vim reformatting it, I usually toggle paste mode: :set paste to turn it on, paste the text, then :set nopaste to go back. I often map a key for that so it’s painless, for example :set pastetoggle= or put in my config nnoremap :set paste! to flip it. Paste mode stops auto-indent, indentexpr, and other niceties, so your pasted code won't get mangled.
If I need to disable automatic indentation for editing (not just pasting), I prefer buffer-local switches so I don’t mess with other files: :setlocal noautoindent nosmartindent nocindent and, if needed, :setlocal indentexpr= to clear any expression-based indent. To restore, use :setlocal autoindent smartindent cindent or reopen the buffer. Little tip: :set paste? shows whether paste is on. Personally, I use paste for quick fixes and :setlocal for longer edits — keeps things predictable and quiet during a frantic refactor.
Man, that frustration is so real — I’ve been there. First thing I do is check whether vim even thinks it should indent: open the file and run :set filetype? and :verbose set autoindent. If filetype is empty or wrong, indent scripts won’t run. If :verbose shows autoindent being turned off by some script, that points to the culprit.
Next, consider obvious toggles that silently kill indentation: if you’ve got 'set paste' enabled (or you toggled paste mode earlier with a mapping), indentation won’t behave. Also check whether you disabled 'autoindent', 'smartindent', or 'cindent' by mistake. Use :set paste? and :set autoindent? to inspect current state.
If those look fine, source your vimrc manually (:source ~/.vimrc) and watch :messages for errors — a syntax error early in the file can stop the rest of the config from loading, so later indent settings never get applied. Also run vim -u NONE (or nvim -u NORC) to see if a vanilla session indents correctly; if it does, a plugin or a line in your vimrc is to blame. Useful commands: :scriptnames (shows loaded scripts), :verbose set shiftwidth? tabstop? expandtab? and checking ~/.vim/indent or plugin ftplugin files for overrides. If you want, paste the problematic snippet and I’ll poke at it with you.
Okay, this is the hot take I give my friends when they ask how to stop JavaScript files from turning into a jagged mess: treat indentation as a filetype thing, not a global, and use 2 spaces plus an actual JS-aware indent engine. I usually put this in my vimrc (or better, in ftplugin/javascript.vim):
filetype plugin indent on
autocmd FileType javascript,typescript setlocal shiftwidth=2 softtabstop=2 tabstop=2 expandtab
autocmd FileType javascript,typescript setlocal autoindent smartindent
Those lines give you consistent 2-space soft tabs (the de facto style for many JS projects) and rely on Vim's smartindent for basic braces. But honestly, for real-world code with ES6/JSX/template literals, install a javascript-indent plugin (like the popular one that provides an indentexpr) and let it set indentexpr for you; it handles arrow functions, template literals and some weird edge cases better than plain smartindent. I also map = to re-indent visually: vmap = = or use gg=G to reformat a whole file.
Finally, I pair this with an on-save formatter — 'prettier' is my go-to — so even when teammates differ, my local formatting is predictable. If you want the exact plugin names or a sample ftplugin that runs Prettier on save, I can paste that too.
Honestly, getting Python auto-indent working in vim is one of those tiny victories that makes editing a joy. My go-to is to enable vim's filetype detection and then set sensible Python indentation rules in my config. Add these lines to your ~/.vimrc or init.vim for Neovim:
filetype plugin indent on
set autoindent
set expandtab
set shiftwidth=4
set softtabstop=4
set tabstop=4
The first line turns on filetype-specific plugins and indent scripts (this loads vim's python indent file). The rest make tabs into spaces and use four spaces per indent, which is the common Python convention. If you want the setting to apply only to Python buffers, drop the global lines into ~/.vim/ftplugin/python.vim and use setlocal instead of set.
If indentation still feels off, check the buffer's filetype with :set filetype? and inspect loaded scripts with :scriptnames. I sometimes install a plugin like 'vim-python-pep8-indent' or use external formatters like 'black' called via a formatter plugin to normalize whitespace. Try opening a .py and typing an indented block — it should behave. If not, tell me what output :set filetype? and :verbose set shiftwidth? give and we can debug further.
Okay, I’ll gush a bit: if you want auto-indent to actually behave instead of randomly guessing, start by combining a detector, a language-aware indenter, and a formatter. I like using vim-sleuth to sniff tabs vs spaces and shiftwidth automatically; it fixes half my headaches before I even open the file.
After sleuth, for Neovim I plug in nvim-treesitter with its indent module turned on — it understands syntax much better than old regex-based indent scripts. Pair that with either null-ls or coc.nvim (or ale if you prefer linters/formatters) to run real formatters like prettier, clang-format, shfmt, or rustfmt on save. That lets the language tools correct structural indentation rather than vim guessing.
Small extras that helped me: editorconfig-vim to respect project settings, indent-o-matic as a fallback detector in weird repos, and indent-blankline.nvim for visual guides so you can spot mistakes. Also don't forget filetype plugin indent on and sensible defaults (autoindent, smartindent/cindent where appropriate). With those layered, indentation accuracy improves dramatically and my diffs stop being a jungle of whitespace edits.
I'll be blunt: yes, you absolutely can set up Vim to auto-indent differently per project directory, and I've done it a bunch of times across projects with different coding styles.
When I need a project-specific policy I usually pick one of three safe routes: use a repository-level '.editorconfig' with the EditorConfig Vim plugin (works across editors and is a huge life-saver), add per-project autocommands in my global vimrc that match the project path, or—if I must—use a controlled local vimrc mechanism (with security checks). For example, in your main vimrc you can add an autocmd that applies settings only when the buffer lives under a particular path:
augroup proj_indent
autocmd!
autocmd BufRead,BufNewFile /path/to/myproj/* setlocal shiftwidth=4 tabstop=4 expandtab
augroup END
That keeps the rules scoped to files under that directory. I avoid blindly enabling 'exrc' because executing arbitrary project .vimrc files can be risky; instead I either require a checked-in '.editorconfig' or use a trusted plugin like 'localvimrc' that prompts you before sourcing. Also remember to use setlocal so other projects aren’t affected. For Neovim, the same autocmds work, but I often detect the project root via an LSP/root_pattern helper and then apply settings dynamically. Overall, choose EditorConfig if you want a cross-editor approach, or autocommands if you prefer staying purely in Vim land.
Okay — let me walk you through this like we’re debugging a stubborn editor together. In my experience inconsistent Vim indentation across buffers usually comes down to a few culprits: buffer-local options, filetype-specific plugins, modelines in files, or external tools like an .editorconfig plugin.
First, check what each buffer actually has set. Use :setlocal and :verbose set shiftwidth? tabstop? softtabstop? expandtab? and :set filetype? and :verbose set autoindent? — the verbose form tells you where a setting was last changed. If you see different values between buffers, that’s your clue: something is changing options per file. Often a ftplugin or indent script is overriding global settings, or a modeline inside a file is setting tabs/spaces.
To fix it, pick a consistent baseline in your vimrc/init.vim: filetype plugin indent on (or in Neovim, enable filetype and indentation early), then set sensible defaults like set tabstop=4 shiftwidth=4 softtabstop=4 expandtab or use set noexpandtab for projects that prefer tabs. If a project has specific rules, add an .editorconfig file and install the editorconfig plugin or add autocmds to apply per-filetype settings. When you need to find the source of an override, :scriptnames shows loaded scripts and :verbose set