aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/editorconfig.lua
diff options
context:
space:
mode:
authorLewis Russell <lewis6991@gmail.com>2024-03-09 12:21:01 +0000
committerLewis Russell <me@lewisr.dev>2024-03-10 23:20:44 +0000
commita09ddd7ce55037edc9747a682810fba6a26bc201 (patch)
tree66f9888cf90c146d7ca366e4c919142d373d73b9 /runtime/lua/editorconfig.lua
parent09a919f313ec8ae691798e45ee459a4467ce5d6a (diff)
downloadrneovim-a09ddd7ce55037edc9747a682810fba6a26bc201.tar.gz
rneovim-a09ddd7ce55037edc9747a682810fba6a26bc201.tar.bz2
rneovim-a09ddd7ce55037edc9747a682810fba6a26bc201.zip
docs(editorconfig): move to source
Diffstat (limited to 'runtime/lua/editorconfig.lua')
-rw-r--r--runtime/lua/editorconfig.lua197
1 files changed, 127 insertions, 70 deletions
diff --git a/runtime/lua/editorconfig.lua b/runtime/lua/editorconfig.lua
index 49d63807a6..6c5c820b0c 100644
--- a/runtime/lua/editorconfig.lua
+++ b/runtime/lua/editorconfig.lua
@@ -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 *.
----
---- 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.
+--- @private
+--- Modified version of [glob2regpat()] that does not match path separators on `*`.
---
----@param glob string Glob to convert into a regular expression
----@return string Regular expression
+--- 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.
---
----@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