diff options
author | Lewis Russell <lewis6991@gmail.com> | 2024-03-09 12:21:01 +0000 |
---|---|---|
committer | Lewis Russell <me@lewisr.dev> | 2024-03-10 23:20:44 +0000 |
commit | a09ddd7ce55037edc9747a682810fba6a26bc201 (patch) | |
tree | 66f9888cf90c146d7ca366e4c919142d373d73b9 /runtime/lua/editorconfig.lua | |
parent | 09a919f313ec8ae691798e45ee459a4467ce5d6a (diff) | |
download | rneovim-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.lua | 197 |
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 |