Skip to content

ftplugins don't set b:undo_ftpluginsetlocal & b:ivim_* state leaks across filetype changes #10

@chiro-hiro

Description

@chiro-hiro

Severity: low · Category: robustness
Location: after/ftplugin/c.vim — whole file (representative of all 15 language ftplugins: c, cpp, css, dockerfile, html, javascript, json, lua, markdown, python, rust, sh, toml, typescript, yaml)

What's wrong

None of the language ftplugins define b:undo_ftplugin, which is the standard Vim mechanism (:help undo_ftplugin, :help ftplugin-overrule) for reverting buffer-local settings when a buffer's filetype changes. Because every ftplugin uses setlocal (e.g. cinoptions, textwidth, colorcolumn, omnifunc, conceallevel) and sets b:ivim_complete_triggers / b:ivim_autocomplete_disable but provides no undo, these persist into the next filetype when the same buffer is re-typed (e.g. :set ft=ruby on a buffer that was c, or any plugin that re-runs filetype detection). This is not merely cosmetic: the autocomplete engine reads the stale value via let l:triggers = get(b:, 'ivim_complete_triggers', ['.']) in plugin/autocomplete.vim, so a buffer switched from c (triggers ['.','>']) to a filetype that has no iVim ftplugin (and thus never reassigns the var) gets the wrong omnifunc trigger set ('>' firing ) applied to the new language. Likewise python's textwidth=88/colorcolumn=88 and c/cpp's cinoptions=l1,g0,:0 remain in effect after switching to an unrelated filetype.

Evidence

after/ftplugin/c.vim is entirely:
setlocal tabstop=4 shiftwidth=4 softtabstop=4 expandtab
setlocal cinoptions=l1,g0,:0
setlocal omnifunc=ccomplete#Complete
let b:ivim_complete_triggers = ['.', '>']
There is no let b:undo_ftplugin = .... grep -rn undo_ftplugin over the whole repo returns nothing. The leak is consumed in plugin/autocomplete.vim:34 let l:triggers = get(b:, 'ivim_complete_triggers', ['.']) — the get() default ['.'] only applies when the var was never set in this buffer, so a stale value from a prior filetype is used verbatim.

Suggested fix

Add a b:undo_ftplugin line to each ftplugin that reverts the options it sets and unlets its b:ivim_* vars, e.g. in c.vim/cpp.vim: let b:undo_ftplugin = 'setlocal ts< sw< sts< et< cino< ofu< | unlet! b:ivim_complete_triggers' (append with | if b:undo_ftplugin may already exist from a shipped ftplugin: let b:undo_ftplugin = get(b:, 'undo_ftplugin', '') . '| setlocal ... | unlet! b:ivim_complete_triggers'). Python additionally reverts tw< cc<; netrw reverts cole< cocu< stl< and clears the buffer mappings. This restores standard Vim ftplugin hygiene and prevents stale trigger/option leakage on filetype change.


Filed from an automated multi-agent source review (2026-05-29); finding adversarially verified at high confidence. Line numbers reflect the audit-fixes-2026-05 working tree.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions