1

While porting my init.vim to init.lua, I noticed some strange behaviour.

In vimscript, one can toggle spell in the following way:

noremap <Leader>ss :setlocal spell!<CR>

Observe that this keymap involves entering command mode, then returning to normal mode.

When porting this to Lua, the most direct translation method works identically:

vim.keymap.set("", "<Leader>ss", ":setlocal spell!<CR>")

However, for the sake of easier-to-read code, I want to set the spell option using the Lua table of values instead.

function ToggleSpell()
        vim.wo.spell = not vim.wo.spell
        print("spell is " .. tostring(vim.wo.spell))
end

vim.keymap.set("", "<Leader>ss", ToggleSpell, { expr = true, })

Now when I type <Leader>ss, a message is printed telling me the state of spell, but the highlighting of misspelled words does not change until I type an additional command (e.g., a motion command, or entering insert mode. Interestingly, entering command mode does not trigger the spelling highlighting until returning to normal mode).

Inlining the function does not change this behaviour:

vim.keymap.set("", "<Leader>st", function ()
        vim.wo.spell = not vim.wo.spell
end, { expr = true, })

Note that running the function directly on the commandline

:lua ToggleSpell()

does result in the highlighting to toggle immediately (perhaps because of the return from command mode to normal mode).

When setting an option in Lua, should I be calling a screen redraw myself? Is there a canonical way of triggering a screen redraw in this case without changing modes?

1 Answer 1

1

Adding an explicit :redraw will solve the problem, and seems to be the standard practice for those writing low-level plugins. (See this similar discussion for :highlight which recommends using :redraw[!].)

function ToggleSpell()
        vim.wo.spell = not vim.wo.spell
        vim.cmd("redraw")
        print("spell is " .. tostring(vim.wo.spell))
end

The big question comes down to whether this actually improves readability. One may argue that :setlocal spell!<CR> more clearly communicates that this mapping toggles spelling in the local window than the function name ToggleSpell. One possible refactor is to rename it to ToggleLocalSpell, or to pass in the scope directly:

function ToggleSpell(scope)
        return function ()
                scope.spell = not scope.spell
                vim.cmd("redraw")
                print("spell is " .. tostring(scope.spell))
        end
end

vim.keymap.set("", "<Leader>ss", ToggleSpell(vim.wo), { expr = true, })

Now it is explicitly clear that the local option is for the window and not the buffer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.