mirror of
https://github.com/neovim/neovim.git
synced 2026-04-05 07:09:23 +00:00
docs(editorconfig): move to source
This commit is contained in:
committed by
Lewis Russell
parent
09a919f313
commit
a09ddd7ce5
@@ -1,31 +1,80 @@
|
||||
local M = {}
|
||||
--- @brief
|
||||
--- Nvim supports EditorConfig. When a file is opened, Nvim searches all parent
|
||||
--- directories of that file for ".editorconfig" files, parses them, and applies
|
||||
--- any properties that match the opened file. Think of it like 'modeline' for an
|
||||
--- entire (recursive) directory. For more information see
|
||||
--- https://editorconfig.org/.
|
||||
---
|
||||
|
||||
--- @brief [g:editorconfig]() [b:editorconfig]()
|
||||
---
|
||||
--- EditorConfig is enabled by default. To disable it, add to your config:
|
||||
--- ```lua
|
||||
--- vim.g.editorconfig = false
|
||||
--- ```
|
||||
---
|
||||
--- (Vimscript: `let g:editorconfig = v:false`). It can also be disabled
|
||||
--- per-buffer by setting the [b:editorconfig] buffer-local variable to `false`.
|
||||
---
|
||||
--- Nvim stores the applied properties in [b:editorconfig] if it is not `false`.
|
||||
|
||||
--- @brief [editorconfig-custom-properties]()
|
||||
---
|
||||
--- New properties can be added by adding a new entry to the "properties" table.
|
||||
--- The table key is a property name and the value is a callback function which
|
||||
--- accepts the number of the buffer to be modified, the value of the property
|
||||
--- in the `.editorconfig` file, and (optionally) a table containing all of the
|
||||
--- other properties and their values (useful for properties which depend on other
|
||||
--- properties). The value is always a string and must be coerced if necessary.
|
||||
--- Example:
|
||||
---
|
||||
--- ```lua
|
||||
---
|
||||
--- require('editorconfig').properties.foo = function(bufnr, val, opts)
|
||||
--- if opts.charset and opts.charset ~= "utf-8" then
|
||||
--- error("foo can only be set when charset is utf-8", 0)
|
||||
--- end
|
||||
--- vim.b[bufnr].foo = val
|
||||
--- end
|
||||
---
|
||||
--- ```
|
||||
|
||||
--- @brief [editorconfig-properties]()
|
||||
---
|
||||
--- The following properties are supported by default:
|
||||
|
||||
--- @type table<string,fun(bufnr: integer, val: string, opts?: table)>
|
||||
M.properties = {}
|
||||
local properties = {}
|
||||
|
||||
--- @private
|
||||
--- Modified version of the builtin assert that does not include error position information
|
||||
---
|
||||
---@param v any Condition
|
||||
---@param message string Error message to display if condition is false or nil
|
||||
---@return any v if not false or nil, otherwise an error is displayed
|
||||
---
|
||||
---@private
|
||||
--- @param v any Condition
|
||||
--- @param message string Error message to display if condition is false or nil
|
||||
--- @return any v if not false or nil, otherwise an error is displayed
|
||||
local function assert(v, message)
|
||||
return v or error(message, 0)
|
||||
end
|
||||
|
||||
--- @private
|
||||
--- Show a warning message
|
||||
---
|
||||
---@param msg string Message to show
|
||||
---
|
||||
---@private
|
||||
--- @param msg string Message to show
|
||||
local function warn(msg, ...)
|
||||
vim.notify_once(string.format(msg, ...), vim.log.levels.WARN, {
|
||||
vim.notify_once(msg:format(...), vim.log.levels.WARN, {
|
||||
title = 'editorconfig',
|
||||
})
|
||||
end
|
||||
|
||||
function M.properties.charset(bufnr, val)
|
||||
--- If "true", then stop searching for `.editorconfig` files in parent
|
||||
--- directories. This property must be at the top-level of the
|
||||
--- `.editorconfig` file (i.e. it must not be within a glob section).
|
||||
function properties.root()
|
||||
-- Unused
|
||||
end
|
||||
|
||||
--- One of `"utf-8"`, `"utf-8-bom"`, `"latin1"`, `"utf-16be"`, or `"utf-16le"`.
|
||||
--- Sets the 'fileencoding' and 'bomb' options.
|
||||
function properties.charset(bufnr, val)
|
||||
assert(
|
||||
vim.list_contains({ 'utf-8', 'utf-8-bom', 'latin1', 'utf-16be', 'utf-16le' }, val),
|
||||
'charset must be one of "utf-8", "utf-8-bom", "latin1", "utf-16be", or "utf-16le"'
|
||||
@@ -40,14 +89,18 @@ function M.properties.charset(bufnr, val)
|
||||
end
|
||||
end
|
||||
|
||||
function M.properties.end_of_line(bufnr, val)
|
||||
--- One of `"lf"`, `"crlf"`, or `"cr"`.
|
||||
--- These correspond to setting 'fileformat' to "unix", "dos", or "mac",
|
||||
--- respectively.
|
||||
function properties.end_of_line(bufnr, val)
|
||||
vim.bo[bufnr].fileformat = assert(
|
||||
({ lf = 'unix', crlf = 'dos', cr = 'mac' })[val],
|
||||
'end_of_line must be one of "lf", "crlf", or "cr"'
|
||||
)
|
||||
end
|
||||
|
||||
function M.properties.indent_style(bufnr, val, opts)
|
||||
--- One of `"tab"` or `"space"`. Sets the 'expandtab' option.
|
||||
function properties.indent_style(bufnr, val, opts)
|
||||
assert(val == 'tab' or val == 'space', 'indent_style must be either "tab" or "space"')
|
||||
vim.bo[bufnr].expandtab = val == 'space'
|
||||
if val == 'tab' and not opts.indent_size then
|
||||
@@ -56,7 +109,11 @@ function M.properties.indent_style(bufnr, val, opts)
|
||||
end
|
||||
end
|
||||
|
||||
function M.properties.indent_size(bufnr, val, opts)
|
||||
--- A number indicating the size of a single indent. Alternatively, use the
|
||||
--- value "tab" to use the value of the tab_width property. Sets the
|
||||
--- 'shiftwidth' and 'softtabstop' options. If this value is not "tab" and
|
||||
--- the tab_width property is not set, 'tabstop' is also set to this value.
|
||||
function properties.indent_size(bufnr, val, opts)
|
||||
if val == 'tab' then
|
||||
vim.bo[bufnr].shiftwidth = 0
|
||||
vim.bo[bufnr].softtabstop = 0
|
||||
@@ -70,11 +127,14 @@ function M.properties.indent_size(bufnr, val, opts)
|
||||
end
|
||||
end
|
||||
|
||||
function M.properties.tab_width(bufnr, val)
|
||||
--- The display size of a single tab character. Sets the 'tabstop' option.
|
||||
function properties.tab_width(bufnr, val)
|
||||
vim.bo[bufnr].tabstop = assert(tonumber(val), 'tab_width must be a number')
|
||||
end
|
||||
|
||||
function M.properties.max_line_length(bufnr, val)
|
||||
--- A number indicating the maximum length of a single
|
||||
--- line. Sets the 'textwidth' option.
|
||||
function properties.max_line_length(bufnr, val)
|
||||
local n = tonumber(val)
|
||||
if n then
|
||||
vim.bo[bufnr].textwidth = n
|
||||
@@ -84,7 +144,8 @@ function M.properties.max_line_length(bufnr, val)
|
||||
end
|
||||
end
|
||||
|
||||
function M.properties.trim_trailing_whitespace(bufnr, val)
|
||||
--- When `"true"`, trailing whitespace is automatically removed when the buffer is written.
|
||||
function properties.trim_trailing_whitespace(bufnr, val)
|
||||
assert(
|
||||
val == 'true' or val == 'false',
|
||||
'trim_trailing_whitespace must be either "true" or "false"'
|
||||
@@ -109,7 +170,9 @@ function M.properties.trim_trailing_whitespace(bufnr, val)
|
||||
end
|
||||
end
|
||||
|
||||
function M.properties.insert_final_newline(bufnr, val)
|
||||
--- `"true"` or `"false"` to ensure the file always has a trailing newline as its last byte.
|
||||
--- Sets the 'fixendofline' and 'endofline' options.
|
||||
function properties.insert_final_newline(bufnr, val)
|
||||
assert(val == 'true' or val == 'false', 'insert_final_newline must be either "true" or "false"')
|
||||
vim.bo[bufnr].fixendofline = val == 'true'
|
||||
|
||||
@@ -128,63 +191,56 @@ function M.properties.insert_final_newline(bufnr, val)
|
||||
end
|
||||
end
|
||||
|
||||
--- Modified version of |glob2regpat()| that does not match path separators on *.
|
||||
--- @private
|
||||
--- Modified version of [glob2regpat()] that does not match path separators on `*`.
|
||||
---
|
||||
--- This function replaces single instances of * with the regex pattern [^/]*. However, the star in
|
||||
--- the replacement pattern also gets interpreted by glob2regpat, so we insert a placeholder, pass
|
||||
--- it through glob2regpat, then replace the placeholder with the actual regex pattern.
|
||||
--- This function replaces single instances of `*` with the regex pattern `[^/]*`.
|
||||
--- However, the star in the replacement pattern also gets interpreted by glob2regpat,
|
||||
--- so we insert a placeholder, pass it through glob2regpat, then replace the
|
||||
--- placeholder with the actual regex pattern.
|
||||
---
|
||||
---@param glob string Glob to convert into a regular expression
|
||||
---@return string Regular expression
|
||||
---
|
||||
---@private
|
||||
--- @param glob string Glob to convert into a regular expression
|
||||
--- @return string regex Regular expression
|
||||
local function glob2regpat(glob)
|
||||
local placeholder = '@@PLACEHOLDER@@'
|
||||
return (
|
||||
string.gsub(
|
||||
vim.fn.glob2regpat(
|
||||
vim.fn.substitute(
|
||||
string.gsub(glob, '{(%d+)%.%.(%d+)}', '[%1-%2]'),
|
||||
'\\*\\@<!\\*\\*\\@!',
|
||||
placeholder,
|
||||
'g'
|
||||
)
|
||||
),
|
||||
placeholder,
|
||||
'[^/]*'
|
||||
)
|
||||
local glob1 = vim.fn.substitute(
|
||||
glob:gsub('{(%d+)%.%.(%d+)}', '[%1-%2]'),
|
||||
'\\*\\@<!\\*\\*\\@!',
|
||||
placeholder,
|
||||
'g'
|
||||
)
|
||||
local regpat = vim.fn.glob2regpat(glob1)
|
||||
return (regpat:gsub(placeholder, '[^/]*'))
|
||||
end
|
||||
|
||||
--- @private
|
||||
--- Parse a single line in an EditorConfig file
|
||||
---
|
||||
---@param line string Line
|
||||
---@return string|nil If the line contains a pattern, the glob pattern
|
||||
---@return string|nil If the line contains a key-value pair, the key
|
||||
---@return string|nil If the line contains a key-value pair, the value
|
||||
---
|
||||
---@private
|
||||
--- @param line string Line
|
||||
--- @return string? glob pattern if the line contains a pattern
|
||||
--- @return string? key if the line contains a key-value pair
|
||||
--- @return string? value if the line contains a key-value pair
|
||||
local function parse_line(line)
|
||||
if line:find('^%s*[^ #;]') then
|
||||
local glob = (line:match('%b[]') or ''):match('^%s*%[(.*)%]%s*$')
|
||||
if glob then
|
||||
return glob, nil, nil
|
||||
end
|
||||
if not line:find('^%s*[^ #;]') then
|
||||
return
|
||||
end
|
||||
|
||||
local key, val = line:match('^%s*([^:= ][^:=]-)%s*[:=]%s*(.-)%s*$')
|
||||
if key ~= nil and val ~= nil then
|
||||
return nil, key:lower(), val:lower()
|
||||
end
|
||||
--- @type string?
|
||||
local glob = (line:match('%b[]') or ''):match('^%s*%[(.*)%]%s*$')
|
||||
if glob then
|
||||
return glob
|
||||
end
|
||||
|
||||
local key, val = line:match('^%s*([^:= ][^:=]-)%s*[:=]%s*(.-)%s*$')
|
||||
if key ~= nil and val ~= nil then
|
||||
return nil, key:lower(), val:lower()
|
||||
end
|
||||
end
|
||||
|
||||
--- Parse options from an .editorconfig file
|
||||
---
|
||||
---@param filepath string File path of the file to apply EditorConfig settings to
|
||||
---@param dir string Current directory
|
||||
---@return table<string,string|boolean> Table of options to apply to the given file
|
||||
---
|
||||
---@private
|
||||
--- @private
|
||||
--- Parse options from an `.editorconfig` file
|
||||
--- @param filepath string File path of the file to apply EditorConfig settings to
|
||||
--- @param dir string Current directory
|
||||
--- @return table<string,string|boolean> Table of options to apply to the given file
|
||||
local function parse(filepath, dir)
|
||||
local pat --- @type vim.regex?
|
||||
local opts = {} --- @type table<string,string|boolean>
|
||||
@@ -215,11 +271,11 @@ local function parse(filepath, dir)
|
||||
return opts
|
||||
end
|
||||
|
||||
--- Configure the given buffer with options from an .editorconfig file
|
||||
---
|
||||
---@param bufnr integer Buffer number to configure
|
||||
---
|
||||
---@private
|
||||
local M = {}
|
||||
|
||||
--- @private
|
||||
--- Configure the given buffer with options from an `.editorconfig` file
|
||||
--- @param bufnr integer Buffer number to configure
|
||||
function M.config(bufnr)
|
||||
bufnr = bufnr or vim.api.nvim_get_current_buf()
|
||||
if not vim.api.nvim_buf_is_valid(bufnr) then
|
||||
@@ -247,8 +303,9 @@ function M.config(bufnr)
|
||||
local applied = {} --- @type table<string,string|boolean>
|
||||
for opt, val in pairs(opts) do
|
||||
if val ~= 'unset' then
|
||||
local func = M.properties[opt]
|
||||
local func = properties[opt]
|
||||
if func then
|
||||
--- @type boolean, string?
|
||||
local ok, err = pcall(func, bufnr, val, opts)
|
||||
if ok then
|
||||
applied[opt] = val
|
||||
|
||||
Reference in New Issue
Block a user