Skip to content

Commit

Permalink
Merge pull request #35 from lukas-reineke/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
lukas-reineke authored Apr 19, 2022
2 parents 454ec5f + 71b779d commit f56ad1b
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 78 deletions.
49 changes: 30 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ LSP-format.nvim is a wrapper around Neovims native LSP formatting.

It does

1. Asynchronously formatting on save
2. Sequentially formatting with all attached LSP server
1. Asynchronous or synchronous formatting on save
2. Sequential formatting with all attached LSP server
3. Add commands for disabling formatting (globally or per filetype)
4. Make it easier to send format options to the LSP
5. Allow you to exclude specific LSP servers from formatting.
Expand All @@ -14,6 +14,10 @@ It does not

1. _Provide any formatting by itself._ You still need to use an LSP server

## Requirements

LSP-format requires Neovim 0.7 or newer.

## Install

Use your favourite plugin manager to install.
Expand Down Expand Up @@ -66,41 +70,49 @@ require "lspconfig".gopls.setup { on_attach = on_attach }

That's it, saving a buffer will format it now.

## Special format options

There are a couple special format options that LSP-fromat uses.

#### `exclude` format option

`exclude` is a table of LSP servers that should not format the buffer.

Alternatively, you can also just not call `on_attach` for the clients you don't want to use for
formatting.

#### `order` format option

`order` is a table that determines the order formatting is requested from the LSP server.

#### `sync` format option

`sync` turns on synchronous formatting. The editor will block until the formatting is done.

## Notes

#### Make sure you remove any old format on save code

You don't want to run formatting twice. If you had setup formatting on save before, remove it.
You can check if something is listening on buffer write events with `:autocmd BufWritePre` and `:autocmd BufWritePost`

#### `:wq` will not format
#### `:wq` will not format when not using `sync`

Because formatting is async now, you can't save and quit in the same command. The formatting results will not get back
Because default formatting is async, you can't save and quit in the same command. The formatting results will not get back
in time and Neovim will close without applying the changes.
In this case you need to use `vim.lsp.buf.formatting_seq_sync()`
In this case you need to use the `sync` format option.

Add this abbreviation into your dotfiles to do the right thing when doing `:wq`

```lua
vim.cmd [[cabbrev wq execute "lua vim.lsp.buf.formatting_seq_sync()" <bar> wq]]
vim.cmd [[cabbrev wq execute "Format sync" <bar> wq]]
```

#### `exclude` format option

`exclude` is a special format option that lists LSP servers that should not format the buffer.

Alternatively, you can also just not call `on_attach` for the clients you don't want to use for
formatting.

#### `order` format option

`order` is a special format option that determines the order formatting is requested from the LSP server.

## FAQ

### How is it different to `autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()`?

The main difference is that LSP-format.nvim is async. It will format on save, _without blocking the editor_.
The main difference is that LSP-format.nvim is async by default. It will format on save, _without blocking the editor_.
And it adds some convenience with disable commands and format options.
But the end result is the same.

Expand Down Expand Up @@ -135,4 +147,3 @@ require "lspconfig".efm.setup {

Now Typescript gets formatted with 4 and YAML with 2 spaces by default.
And you can run `:Format tab_width=8` to overwrite the setting and format with 8 spaces.

93 changes: 62 additions & 31 deletions doc/format.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@


Author: Lukas Reineke <lukas@reineke.jp>
Version: 2.2.3
Version: 2.3.0

==============================================================================
CONTENTS *lsp-format*

1. Introduction |lsp-format-introduction|
2. Setup |lsp-format-setup|
3. Commands |lsp-format-commands|
4. Changelog |lsp-format-changelog|
5. License |lsp-format-license|
3. Special Format Options |lsp-format-special-format-options|
4. Commands |lsp-format-commands|
5. Changelog |lsp-format-changelog|
6. License |lsp-format-license|

==============================================================================
1. INTRODUCTION *lsp-format-introduction*
Expand All @@ -20,10 +21,11 @@ LSP-format.nvim is a wrapper around Neovims native LSP formatting.

It does

1. Asynchronously formatting on save
2. Sequentially formatting with all attached LSP server
1. Asynchronous or synchronous formatting on save
2. Sequential formatting with all attached LSP server
3. Add commands for disabling formatting (globally or per filetype)
4. Makes it easier to send format options to the LSP
4. Make it easier to send format options to the LSP
5. Allow you to exclude specific LSP servers from formatting.

It does not

Expand All @@ -38,32 +40,55 @@ function to each LSP that should use it.
The setup functions takes one optional argument that maps |filetypes| to
format options.

`exclude` is a special format option, a list of client names to exclude from
formatting.
>
require "lsp-format".setup {
typescript = {
exclude = { "tsserver" }
}
}

==============================================================================
3. SPECIAL FORMAT OPTIONS *lsp-format-special-format-options*

`order` is a special format option, a list of client names. Formatting is
requested from clients in the following order: first all clients that are not
in the `order` list, then the remaining clients in the order as they occur in
the `order` list. (same logic as |vim.lsp.buf.formatting_seq_sync()|)
>
require "lsp-format".setup {
lua = {
indent_width = 4,
},
golang = {
order = { "gopls", "efm" }
}
}
exclude *lsp-format-exclude*

`exclude` is a table of client names to exclude from formatting.

Example: >
require "lsp-format".setup {
go = {
exclude = { "gopls" }
}
}
------------------------------------------------------------------------------
order *lsp-format-order*

`order` is a table of client names. Formatting is requested from clients in
the following order: first all clients that are not in the `order` table,
then the remaining clients in the order as they occur in the `order` table.
(same logic as |vim.lsp.buf.formatting_seq_sync()|)

Example: >
require "lsp-format".setup {
go = {
order = { "gopls", "efm" }
}
}
------------------------------------------------------------------------------
sync *lsp-format-sync*

`sync` is a boolean flag to turn on synchronous formatting. The editor will
block until formatting is done.

Example: >
require "lsp-format".setup {
go = {
sync = true
}
}
==============================================================================
3. COMMANDS *lsp-format-commands*
4. COMMANDS *lsp-format-commands*

:Format [{format_options}] *Format*

Expand Down Expand Up @@ -94,7 +119,13 @@ the `order` list. (same logic as |vim.lsp.buf.formatting_seq_sync()|)
If no filetype is given, it uses the global state.

==============================================================================
4. CHANGELOG *lsp-format-changelog*
5. CHANGELOG *lsp-format-changelog*

2.3.0
Add `sync` option
Fix overwriting global format options from command
Fix empty queue when excluding all clients
Fix wrong index when excluding clients

2.2.3
Don't format with client if `on_attach` was not called for that client
Expand Down Expand Up @@ -138,7 +169,7 @@ the `order` list. (same logic as |vim.lsp.buf.formatting_seq_sync()|)
* First release

==============================================================================
5. LICENSE *lsp-format-license*
6. LICENSE *lsp-format-license*

The MIT Licence
http://www.opensource.org/licenses/mit-license.php
Expand Down
84 changes: 56 additions & 28 deletions lua/lsp-format/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,32 @@ local M = {
M.setup = function(format_options)
M.format_options = vim.tbl_deep_extend("force", M.format_options, format_options or {})

vim.cmd [[command! -nargs=* -bar Format lua require'lsp-format'.format("<args>")]]
vim.cmd [[command! -nargs=? -complete=filetype -bar FormatToggle lua require'lsp-format'.toggle(<q-args>)]]
vim.cmd [[command! -nargs=? -complete=filetype -bar FormatDisable lua require'lsp-format'.disable(<q-args>)]]
vim.cmd [[command! -nargs=? -complete=filetype -bar -bang FormatEnable lua require'lsp-format'.enable(<q-args>, "<bang>" == "!")]]
vim.api.nvim_create_user_command("Format", M.format, { nargs = "*", bar = true, force = true })
vim.api.nvim_create_user_command(
"FormatToggle",
M.toggle,
{ nargs = "?", bar = true, complete = "filetype", force = true }
)
vim.api.nvim_create_user_command(
"FormatDisable",
M.disable,
{ nargs = "?", bar = true, complete = "filetype", force = true }
)
vim.api.nvim_create_user_command(
"FormatEnable",
M.enable,
{ nargs = "?", bar = true, complete = "filetype", force = true, bang = true }
)
end

M.format = function(format_options_string)
M.format = function(options)
if vim.b.format_saving or M.disabled or M.disabled_filetypes[vim.bo.filetype] then
return
end

local bufnr = vim.api.nvim_get_current_buf()
local format_options = M.format_options[vim.bo.filetype] or {}
for _, option in ipairs(vim.split(format_options_string or "", " ")) do
local format_options = vim.deepcopy(M.format_options[vim.bo.filetype] or {})
for _, option in ipairs(options.fargs or {}) do
local key, value = unpack(vim.split(option, "="))
if key == "order" or key == "exclude" then
value = vim.split(value, ",")
Expand All @@ -31,10 +43,10 @@ M.format = function(format_options_string)
end

local clients = vim.tbl_values(vim.lsp.buf_get_clients())
for i, client in pairs(clients) do
for i = #clients, 1, -1 do
if
vim.tbl_contains(format_options.exclude or {}, client.name)
or not vim.tbl_contains(M.buffers[bufnr] or {}, client.id)
vim.tbl_contains(format_options.exclude or {}, clients[i].name)
or not vim.tbl_contains(M.buffers[bufnr] or {}, clients[i].id)
then
table.remove(clients, i)
end
Expand All @@ -49,35 +61,37 @@ M.format = function(format_options_string)
end
end

table.insert(M.queue, { bufnr = bufnr, clients = clients, format_options = format_options })
if #clients > 0 then
table.insert(M.queue, { bufnr = bufnr, clients = clients, format_options = format_options })
end

M._next()
end

M.disable = function(filetype)
if filetype == "" then
M.disable = function(options)
if options.args == "" then
M.disabled = true
else
M.disabled_filetypes[filetype] = true
M.disabled_filetypes[options.args] = true
end
end

M.enable = function(filetype, bang)
if bang then
M.enable = function(options)
if options.bang then
M.disabled_filetypes = {}
M.disabled = false
elseif filetype == "" then
elseif options.args == "" then
M.disabled = false
else
M.disabled_filetypes[filetype] = false
M.disabled_filetypes[options.args] = false
end
end

M.toggle = function(filetype)
if filetype == "" then
M.toggle = function(options)
if options.args == "" then
M.disabled = not M.disabled
else
M.disabled_filetypes[filetype] = not M.disabled_filetypes[filetype]
M.disabled_filetypes[options.args] = not M.disabled_filetypes[options.args]
end
end

Expand All @@ -87,12 +101,19 @@ M.on_attach = function(client)
M.buffers[bufnr] = {}
end
table.insert(M.buffers[bufnr], client.id)
vim.cmd [[
augroup Format
autocmd! * <buffer>
autocmd BufWritePost <buffer> lua require'lsp-format'.format()
augroup END
]]
local format_options = M.format_options[vim.bo.filetype] or {}

local event = "BufWritePost"
if format_options.sync then
event = "BufWritePre"
end

vim.api.nvim_create_autocmd(event, {
group = vim.api.nvim_create_augroup("Format", {}),
desc = "format on save",
pattern = "<buffer>",
callback = M.format,
})
end

M._handler = function(err, result, ctx)
Expand Down Expand Up @@ -127,7 +148,14 @@ end
M._format = function(bufnr, client, format_options)
vim.b.format_changedtick = vim.b.changedtick
local params = vim.lsp.util.make_formatting_params(format_options)
client.request("textDocument/formatting", params, M._handler, bufnr)
local method = "textDocument/formatting"
local timeout_ms = 2000
if format_options.sync then
local result = client.request_sync(method, params, timeout_ms, bufnr) or {}
M._handler(result.err, result.result, { client_id = client.id, bufnr = bufnr })
else
client.request(method, params, M._handler, bufnr)
end
end

M._next = function()
Expand Down

0 comments on commit f56ad1b

Please sign in to comment.