From ab9a2c49253413dbbb31756a3eeddb354a663035 Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Tue, 3 Jan 2023 09:13:18 -0700 Subject: feat(editorconfig): add builtin EditorConfig support --- runtime/lua/editorconfig.lua | 246 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 runtime/lua/editorconfig.lua (limited to 'runtime/lua') diff --git a/runtime/lua/editorconfig.lua b/runtime/lua/editorconfig.lua new file mode 100644 index 0000000000..2079006234 --- /dev/null +++ b/runtime/lua/editorconfig.lua @@ -0,0 +1,246 @@ +local M = {} + +M.properties = {} + +--- 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 +local function assert(v, message) + return v or error(message, 0) +end + +--- Show a warning message +--- +---@param msg string Message to show +--- +---@private +local function warn(msg, ...) + vim.notify(string.format(msg, ...), vim.log.levels.WARN, { + title = 'editorconfig', + }) +end + +function M.properties.charset(bufnr, val) + assert( + vim.tbl_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"' + ) + if val == 'utf-8' or val == 'utf-8-bom' then + vim.bo[bufnr].fileencoding = 'utf-8' + vim.bo[bufnr].bomb = val == 'utf-8-bom' + elseif val == 'utf-16be' then + vim.bo[bufnr].fileencoding = 'utf-16' + else + vim.bo[bufnr].fileencoding = val + end +end + +function M.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) + 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 + vim.bo[bufnr].shiftwidth = 0 + vim.bo[bufnr].softtabstop = 0 + end +end + +function M.properties.indent_size(bufnr, val, opts) + if val == 'tab' then + vim.bo[bufnr].shiftwidth = 0 + vim.bo[bufnr].softtabstop = 0 + else + local n = assert(tonumber(val), 'indent_size must be a number') + vim.bo[bufnr].shiftwidth = n + vim.bo[bufnr].softtabstop = -1 + if not opts.tab_width then + vim.bo[bufnr].tabstop = n + end + end +end + +function M.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) + local n = tonumber(val) + if n then + vim.bo[bufnr].textwidth = n + else + assert(val == 'off', 'max_line_length must be a number or "off"') + vim.bo[bufnr].textwidth = 0 + end +end + +function M.properties.trim_trailing_whitespace(bufnr, val) + assert( + val == 'true' or val == 'false', + 'trim_trailing_whitespace must be either "true" or "false"' + ) + if val == 'true' then + vim.api.nvim_create_autocmd('BufWritePre', { + group = 'editorconfig', + buffer = bufnr, + callback = function() + local view = vim.fn.winsaveview() + vim.api.nvim_command('silent! undojoin') + vim.api.nvim_command('silent keepjumps keeppatterns %s/\\s\\+$//e') + vim.fn.winrestview(view) + end, + }) + else + vim.api.nvim_clear_autocmds({ + event = 'BufWritePre', + group = 'editorconfig', + buffer = bufnr, + }) + end +end + +function M.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' + vim.bo[bufnr].endofline = val == 'true' +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. +--- +---@param glob string Glob to convert into a regular expression +---@return string Regular expression +--- +---@private +local function glob2regpat(glob) + local placeholder = '@@PLACEHOLDER@@' + return ( + string.gsub( + vim.fn.glob2regpat( + vim.fn.substitute( + string.gsub(glob, '{(%d+)%.%.(%d+)}', '[%1-%2]'), + '\\*\\@ Date: Tue, 3 Jan 2023 09:13:35 -0700 Subject: feat(editorconfig): add editorconfig syntax file This is intentionally _not_ copied from Vim because our syntax file makes use of Lua to dynamically generate a list of valid EditorConfig properties. This requires the builtin editorconfig module, which Vim does not have. --- runtime/lua/vim/filetype.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 7eec567b66..c3ab39a1a3 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1378,13 +1378,13 @@ local filename = { npmrc = 'dosini', ['/etc/yum.conf'] = 'dosini', ['.npmrc'] = 'dosini', - ['.editorconfig'] = 'dosini', ['/etc/pacman.conf'] = 'confini', ['mpv.conf'] = 'confini', dune = 'dune', jbuild = 'dune', ['dune-workspace'] = 'dune', ['dune-project'] = 'dune', + ['.editorconfig'] = 'editorconfig', ['elinks.conf'] = 'elinks', ['mix.lock'] = 'elixir', ['filter-rules'] = 'elmfilt', -- cgit