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.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"' ) 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]'), '\\*\\@