Skip to content

[idea] Wire vim.lsp.formatexpr() so gq uses the LSP/conform formatter #127

Description

@stanfish06

What

Set formatexpr in LspAttach to v:lua.vim.lsp.formatexpr() so that the gq operator delegates to the attached LSP (or conform, which the config already wires as LSP fallback) instead of the built-in line-breaking algorithm.

Where

lua/config/plugin_config.lua — LspAttach autocmd, lines 104–115.

Why it matters

The config already invests in formatting quality: conform.nvim with stylua, ruff_format, prettier; default_format_opts = { lsp_format = "fallback" }; and a custom <leader>lf keymap. But gq (reformat operator / motion) still falls back to Vim's dumb line-wrapper, ignoring all of that. For prose-heavy work (Markdown notes via obsidian.nvim, inline comments) gqq / gqip / gq<motion> are natural muscle-memory, and they currently produce inconsistent results vs <leader>lf.

Neovim 0.11+ ships vim.lsp.formatexpr() exactly for this purpose.

Suggested implementation

Inside the LspAttach callback (alongside the existing gd keymap):

vim.api.nvim_create_autocmd("LspAttach", {
    callback = function(ev)
        local client = vim.lsp.get_client_by_id(ev.data.client_id)
        -- existing completion + gd setup …
        vim.bo[ev.buf].formatexpr = "v:lua.vim.lsp.formatexpr()"
    end,
})

vim.lsp.formatexpr() honours the LSP textDocument/rangeFormatting method when the attached server supports it; otherwise it falls back to the built-in algorithm — so there is no regression for buffers with no LSP attached.

Note: this interacts with formatoptions. If the t or c flags are set (they are currently absent, which is correct), the formatexpr fires on auto-wrap too. With the current rqnl1j flags, formatexpr only fires on explicit gq calls, which is the desired behavior.

Activity

  1. stanfish06 commented on Jul 26, 2026

    @stanfish06
    OwnerAuthor

    Fixed in 3cfb0cf.

    Note the location in this issue was wrong: the LspAttach autocmd is in lua/config/lsp.lua:141, not plugin_config.lua:104-115 (those lines are the conform setup). Added there:

    vim.bo[ev.buf].formatexpr = "v:lua.vim.lsp.formatexpr()"

    Verified against a real attached client in headless nvim — after luals attaches, vim.bo[buf].formatexpr reads v:lua.vim.lsp.formatexpr(), so gq/gqq/gqip now route through the LSP formatter with conform as the lsp_format = "fallback" path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions