diff options
author | Josh Rahm <rahm@google.com> | 2022-10-11 19:00:52 +0000 |
---|---|---|
committer | Josh Rahm <rahm@google.com> | 2022-10-11 19:00:52 +0000 |
commit | 21e2e46242033c7aaa6ccfb23e256680816c063c (patch) | |
tree | f089522cfb145d6e9c8a86a01d8e454ce5501e20 /runtime/lua/vim/_meta.lua | |
parent | 179d3ed87b17988f5fe00d8b99f2611a28212be7 (diff) | |
parent | 760b399f6c0c6470daa0663752bd22886997f9e6 (diff) | |
download | rneovim-floattitle.tar.gz rneovim-floattitle.tar.bz2 rneovim-floattitle.zip |
Merge remote-tracking branch 'upstream/master' into floattitlefloattitle
Diffstat (limited to 'runtime/lua/vim/_meta.lua')
-rw-r--r-- | runtime/lua/vim/_meta.lua | 869 |
1 files changed, 346 insertions, 523 deletions
diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index f1652718ee..9c7972873e 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -1,173 +1,113 @@ --- prevents luacheck from making lints for setting things on vim -local vim = assert(vim) - local a = vim.api -local validate = vim.validate - -local SET_TYPES = setmetatable({ - SET = 0, - LOCAL = 1, - GLOBAL = 2, -}, { __index = error }) - -local options_info = nil -local buf_options = nil -local glb_options = nil -local win_options = nil - -local function _setup() - if options_info ~= nil then - return - end - options_info = {} - for _, v in pairs(a.nvim_get_all_options_info()) do - options_info[v.name] = v - if v.shortname ~= '' then - options_info[v.shortname] = v - end - end - local function get_scoped_options(scope) - local result = {} - for name, option_info in pairs(options_info) do - if option_info.scope == scope then - result[name] = true +-- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded. +-- Can be done in a separate PR. +local key_value_options = { + fillchars = true, + fcs = true, + listchars = true, + lcs = true, + winhighlight = true, + winhl = true, +} + +--- Convert a vimoption_T style dictionary to the correct OptionType associated with it. +---@return string +local function get_option_metatype(name, info) + if info.type == 'string' then + if info.flaglist then + return 'set' + elseif info.commalist then + if key_value_options[name] then + return 'map' end + return 'array' end - - return result + return 'string' end - - buf_options = get_scoped_options('buf') - glb_options = get_scoped_options('global') - win_options = get_scoped_options('win') + return info.type end -local function make_meta_accessor(get, set, del, validator) - validator = validator or function() - return true - end - - validate({ - get = { get, 'f' }, - set = { set, 'f' }, - del = { del, 'f', true }, - validator = { validator, 'f' }, - }) +local options_info = setmetatable({}, { + __index = function(t, k) + local info = a.nvim_get_option_info(k) + info.metatype = get_option_metatype(k, info) + rawset(t, k, info) + return rawget(t, k) + end, +}) - local mt = {} - function mt:__newindex(k, v) - if not validator(k) then - return +vim.env = setmetatable({}, { + __index = function(_, k) + local v = vim.fn.getenv(k) + if v == vim.NIL then + return nil end + return v + end, - if del and v == nil then - return del(k) - end - return set(k, v) - end - function mt:__index(k) - if not validator(k) then - return - end + __newindex = function(_, k, v) + vim.fn.setenv(k, v) + end, +}) - return get(k) +local function opt_validate(option_name, target_scope) + local scope = options_info[option_name].scope + if scope ~= target_scope then + local scope_to_string = { buf = 'buffer', win = 'window' } + error( + string.format( + [['%s' is a %s option, not a %s option. See ":help %s"]], + option_name, + scope_to_string[scope] or scope, + scope_to_string[target_scope] or target_scope, + option_name + ) + ) end - return setmetatable({}, mt) end -vim.env = make_meta_accessor(function(k) - local v = vim.fn.getenv(k) - if v == vim.NIL then - return nil - end - return v -end, vim.fn.setenv) - -do -- buffer option accessor - local function new_buf_opt_accessor(bufnr) - local function get(k) - if bufnr == nil and type(k) == 'number' then - return new_buf_opt_accessor(k) - end - - return a.nvim_get_option_value(k, { buf = bufnr or 0 }) - end - - local function set(k, v) - return a.nvim_set_option_value(k, v, { buf = bufnr or 0 }) - end - - return make_meta_accessor(get, set, nil, function(k) - if type(k) == 'string' then - _setup() - if win_options[k] then - error( - string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k) - ) - elseif glb_options[k] then - error( - string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k) - ) - end +local function new_opt_accessor(handle, scope) + return setmetatable({}, { + __index = function(_, k) + if handle == nil and type(k) == 'number' then + return new_opt_accessor(k, scope) end + opt_validate(k, scope) + return a.nvim_get_option_value(k, { [scope] = handle or 0 }) + end, - return true - end) - end - - vim.bo = new_buf_opt_accessor(nil) + __newindex = function(_, k, v) + opt_validate(k, scope) + return a.nvim_set_option_value(k, v, { [scope] = handle or 0 }) + end, + }) end -do -- window option accessor - local function new_win_opt_accessor(winnr) - local function get(k) - if winnr == nil and type(k) == 'number' then - return new_win_opt_accessor(k) - end - return a.nvim_get_option_value(k, { win = winnr or 0 }) - end - - local function set(k, v) - return a.nvim_set_option_value(k, v, { win = winnr or 0 }) - end - - return make_meta_accessor(get, set, nil, function(k) - if type(k) == 'string' then - _setup() - if buf_options[k] then - error( - string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k) - ) - elseif glb_options[k] then - error( - string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k) - ) - end - end - - return true - end) - end - - vim.wo = new_win_opt_accessor(nil) -end +vim.bo = new_opt_accessor(nil, 'buf') +vim.wo = new_opt_accessor(nil, 'win') -- vim global option -- this ONLY sets the global option. like `setglobal` -vim.go = make_meta_accessor(function(k) - return a.nvim_get_option_value(k, { scope = 'global' }) -end, function(k, v) - return a.nvim_set_option_value(k, v, { scope = 'global' }) -end) +vim.go = setmetatable({}, { + __index = function(_, k) + return a.nvim_get_option_value(k, { scope = 'global' }) + end, + __newindex = function(_, k, v) + return a.nvim_set_option_value(k, v, { scope = 'global' }) + end, +}) -- vim `set` style options. -- it has no additional metamethod magic. -vim.o = make_meta_accessor(function(k) - return a.nvim_get_option_value(k, {}) -end, function(k, v) - return a.nvim_set_option_value(k, v, {}) -end) +vim.o = setmetatable({}, { + __index = function(_, k) + return a.nvim_get_option_value(k, {}) + end, + __newindex = function(_, k, v) + return a.nvim_set_option_value(k, v, {}) + end, +}) ---@brief [[ --- vim.opt, vim.opt_local and vim.opt_global implementation @@ -178,11 +118,8 @@ end) ---@brief ]] --- Preserves the order and does not mutate the original list -local remove_duplicate_values = function(t) +local function remove_duplicate_values(t) local result, seen = {}, {} - if type(t) == 'function' then - error(debug.traceback('asdf')) - end for _, v in ipairs(t) do if not seen[v] then table.insert(result, v) @@ -194,61 +131,6 @@ local remove_duplicate_values = function(t) return result end --- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded. --- Can be done in a separate PR. -local key_value_options = { - fillchars = true, - listchars = true, - winhl = true, -} - ----@class OptionTypes ---- Option Type Enum -local OptionTypes = setmetatable({ - BOOLEAN = 0, - NUMBER = 1, - STRING = 2, - ARRAY = 3, - MAP = 4, - SET = 5, -}, { - __index = function(_, k) - error('Not a valid OptionType: ' .. k) - end, - __newindex = function(_, k) - error('Cannot set a new OptionType: ' .. k) - end, -}) - ---- Convert a vimoption_T style dictionary to the correct OptionType associated with it. ----@return OptionType -local get_option_type = function(name, info) - if info.type == 'boolean' then - return OptionTypes.BOOLEAN - elseif info.type == 'number' then - return OptionTypes.NUMBER - elseif info.type == 'string' then - if not info.commalist and not info.flaglist then - return OptionTypes.STRING - end - - if key_value_options[name] then - assert(info.commalist, 'Must be a comma list to use key:value style') - return OptionTypes.MAP - end - - if info.flaglist then - return OptionTypes.SET - elseif info.commalist then - return OptionTypes.ARRAY - end - - error('Fallthrough in OptionTypes') - else - error('Not a known info.type:' .. info.type) - end -end - -- Check whether the OptionTypes is allowed for vim.opt -- If it does not match, throw an error which indicates which option causes the error. local function assert_valid_value(name, value, types) @@ -269,373 +151,323 @@ local function assert_valid_value(name, value, types) ) end +local function passthrough(_, x) + return x +end + +local function tbl_merge(left, right) + return vim.tbl_extend('force', left, right) +end + +local function tbl_remove(t, value) + if type(value) == 'string' then + t[value] = nil + else + for _, v in ipairs(value) do + t[v] = nil + end + end + + return t +end + local valid_types = { - [OptionTypes.BOOLEAN] = { 'boolean' }, - [OptionTypes.NUMBER] = { 'number' }, - [OptionTypes.STRING] = { 'string' }, - [OptionTypes.SET] = { 'string', 'table' }, - [OptionTypes.ARRAY] = { 'string', 'table' }, - [OptionTypes.MAP] = { 'string', 'table' }, + boolean = { 'boolean' }, + number = { 'number' }, + string = { 'string' }, + set = { 'string', 'table' }, + array = { 'string', 'table' }, + map = { 'string', 'table' }, } ---- Convert a lua value to a vimoption_T value -local convert_value_to_vim = (function() - -- Map of functions to take a Lua style value and convert to vimoption_T style value. - -- Each function takes (info, lua_value) -> vim_value - local to_vim_value = { - [OptionTypes.BOOLEAN] = function(_, value) - return value - end, - [OptionTypes.NUMBER] = function(_, value) - return value - end, - [OptionTypes.STRING] = function(_, value) - return value - end, - - [OptionTypes.SET] = function(info, value) - if type(value) == 'string' then - return value - end +-- Map of functions to take a Lua style value and convert to vimoption_T style value. +-- Each function takes (info, lua_value) -> vim_value +local to_vim_value = { + boolean = passthrough, + number = passthrough, + string = passthrough, - if info.flaglist and info.commalist then - local keys = {} - for k, v in pairs(value) do - if v then - table.insert(keys, k) - end - end + set = function(info, value) + if type(value) == 'string' then + return value + end - table.sort(keys) - return table.concat(keys, ',') - else - local result = '' - for k, v in pairs(value) do - if v then - result = result .. k - end + if info.flaglist and info.commalist then + local keys = {} + for k, v in pairs(value) do + if v then + table.insert(keys, k) end - - return result end - end, - [OptionTypes.ARRAY] = function(info, value) - if type(value) == 'string' then - return value - end - if not info.allows_duplicates then - value = remove_duplicate_values(value) + table.sort(keys) + return table.concat(keys, ',') + else + local result = '' + for k, v in pairs(value) do + if v then + result = result .. k + end end - return table.concat(value, ',') - end, - [OptionTypes.MAP] = function(_, value) - if type(value) == 'string' then - return value - end + return result + end + end, - local result = {} - for opt_key, opt_value in pairs(value) do - table.insert(result, string.format('%s:%s', opt_key, opt_value)) - end + array = function(info, value) + if type(value) == 'string' then + return value + end + if not info.allows_duplicates then + value = remove_duplicate_values(value) + end + return table.concat(value, ',') + end, - table.sort(result) - return table.concat(result, ',') - end, - } + map = function(_, value) + if type(value) == 'string' then + return value + end - return function(name, info, value) - if value == nil then - return vim.NIL + local result = {} + for opt_key, opt_value in pairs(value) do + table.insert(result, string.format('%s:%s', opt_key, opt_value)) end - local option_type = get_option_type(name, info) - assert_valid_value(name, value, valid_types[option_type]) + table.sort(result) + return table.concat(result, ',') + end, +} - return to_vim_value[option_type](info, value) +--- Convert a lua value to a vimoption_T value +local function convert_value_to_vim(name, info, value) + if value == nil then + return vim.NIL end -end)() ---- Converts a vimoption_T style value to a Lua value -local convert_value_to_lua = (function() - -- Map of OptionType to functions that take vimoption_T values and convert to lua values. - -- Each function takes (info, vim_value) -> lua_value - local to_lua_value = { - [OptionTypes.BOOLEAN] = function(_, value) - return value - end, - [OptionTypes.NUMBER] = function(_, value) - return value - end, - [OptionTypes.STRING] = function(_, value) - return value - end, + assert_valid_value(name, value, valid_types[info.metatype]) - [OptionTypes.ARRAY] = function(info, value) - if type(value) == 'table' then - if not info.allows_duplicates then - value = remove_duplicate_values(value) - end + return to_vim_value[info.metatype](info, value) +end - return value - end +-- Map of OptionType to functions that take vimoption_T values and convert to lua values. +-- Each function takes (info, vim_value) -> lua_value +local to_lua_value = { + boolean = passthrough, + number = passthrough, + string = passthrough, - -- Empty strings mean that there is nothing there, - -- so empty table should be returned. - if value == '' then - return {} + array = function(info, value) + if type(value) == 'table' then + if not info.allows_duplicates then + value = remove_duplicate_values(value) end - -- Handles unescaped commas in a list. - if string.find(value, ',,,') then - local comma_split = vim.split(value, ',,,') - local left = comma_split[1] - local right = comma_split[2] + return value + end - local result = {} - vim.list_extend(result, vim.split(left, ',')) - table.insert(result, ',') - vim.list_extend(result, vim.split(right, ',')) + -- Empty strings mean that there is nothing there, + -- so empty table should be returned. + if value == '' then + return {} + end - table.sort(result) + -- Handles unescaped commas in a list. + if string.find(value, ',,,') then + local left, right = unpack(vim.split(value, ',,,')) - return result - end + local result = {} + vim.list_extend(result, vim.split(left, ',')) + table.insert(result, ',') + vim.list_extend(result, vim.split(right, ',')) - if string.find(value, ',^,,', 1, true) then - local comma_split = vim.split(value, ',^,,', true) - local left = comma_split[1] - local right = comma_split[2] + table.sort(result) - local result = {} - vim.list_extend(result, vim.split(left, ',')) - table.insert(result, '^,') - vim.list_extend(result, vim.split(right, ',')) + return result + end - table.sort(result) + if string.find(value, ',^,,', 1, true) then + local left, right = unpack(vim.split(value, ',^,,', true)) - return result - end + local result = {} + vim.list_extend(result, vim.split(left, ',')) + table.insert(result, '^,') + vim.list_extend(result, vim.split(right, ',')) - return vim.split(value, ',') - end, + table.sort(result) - [OptionTypes.SET] = function(info, value) - if type(value) == 'table' then - return value - end + return result + end - -- Empty strings mean that there is nothing there, - -- so empty table should be returned. - if value == '' then - return {} - end + return vim.split(value, ',') + end, - assert(info.flaglist, 'That is the only one I know how to handle') + set = function(info, value) + if type(value) == 'table' then + return value + end - if info.flaglist and info.commalist then - local split_value = vim.split(value, ',') - local result = {} - for _, v in ipairs(split_value) do - result[v] = true - end + -- Empty strings mean that there is nothing there, + -- so empty table should be returned. + if value == '' then + return {} + end - return result - else - local result = {} - for i = 1, #value do - result[value:sub(i, i)] = true - end + assert(info.flaglist, 'That is the only one I know how to handle') - return result + if info.flaglist and info.commalist then + local split_value = vim.split(value, ',') + local result = {} + for _, v in ipairs(split_value) do + result[v] = true end - end, - [OptionTypes.MAP] = function(info, raw_value) - if type(raw_value) == 'table' then - return raw_value + return result + else + local result = {} + for i = 1, #value do + result[value:sub(i, i)] = true end - assert(info.commalist, 'Only commas are supported currently') - - local result = {} + return result + end + end, - local comma_split = vim.split(raw_value, ',') - for _, key_value_str in ipairs(comma_split) do - local key, value = unpack(vim.split(key_value_str, ':')) - key = vim.trim(key) + map = function(info, raw_value) + if type(raw_value) == 'table' then + return raw_value + end - result[key] = value - end + assert(info.commalist, 'Only commas are supported currently') - return result - end, - } + local result = {} - return function(name, info, option_value) - return to_lua_value[get_option_type(name, info)](info, option_value) - end -end)() + local comma_split = vim.split(raw_value, ',') + for _, key_value_str in ipairs(comma_split) do + local key, value = unpack(vim.split(key_value_str, ':')) + key = vim.trim(key) ---- Handles the mutation of various different values. -local value_mutator = function(name, info, current, new, mutator) - return mutator[get_option_type(name, info)](current, new) -end + result[key] = value + end ---- Handles the '^' operator -local prepend_value = (function() - local methods = { - [OptionTypes.NUMBER] = function() - error("The '^' operator is not currently supported for") - end, + return result + end, +} - [OptionTypes.STRING] = function(left, right) - return right .. left - end, +--- Converts a vimoption_T style value to a Lua value +local function convert_value_to_lua(info, option_value) + return to_lua_value[info.metatype](info, option_value) +end - [OptionTypes.ARRAY] = function(left, right) - for i = #right, 1, -1 do - table.insert(left, 1, right[i]) - end +local prepend_methods = { + number = function() + error("The '^' operator is not currently supported for") + end, - return left - end, + string = function(left, right) + return right .. left + end, - [OptionTypes.MAP] = function(left, right) - return vim.tbl_extend('force', left, right) - end, + array = function(left, right) + for i = #right, 1, -1 do + table.insert(left, 1, right[i]) + end - [OptionTypes.SET] = function(left, right) - return vim.tbl_extend('force', left, right) - end, - } + return left + end, - return function(name, info, current, new) - return value_mutator( - name, - info, - convert_value_to_lua(name, info, current), - convert_value_to_lua(name, info, new), - methods - ) - end -end)() + map = tbl_merge, + set = tbl_merge, +} ---- Handles the '+' operator -local add_value = (function() - local methods = { - [OptionTypes.NUMBER] = function(left, right) - return left + right - end, +--- Handles the '^' operator +local function prepend_value(info, current, new) + return prepend_methods[info.metatype]( + convert_value_to_lua(info, current), + convert_value_to_lua(info, new) + ) +end - [OptionTypes.STRING] = function(left, right) - return left .. right - end, +local add_methods = { + number = function(left, right) + return left + right + end, - [OptionTypes.ARRAY] = function(left, right) - for _, v in ipairs(right) do - table.insert(left, v) - end + string = function(left, right) + return left .. right + end, - return left - end, + array = function(left, right) + for _, v in ipairs(right) do + table.insert(left, v) + end - [OptionTypes.MAP] = function(left, right) - return vim.tbl_extend('force', left, right) - end, + return left + end, - [OptionTypes.SET] = function(left, right) - return vim.tbl_extend('force', left, right) - end, - } + map = tbl_merge, + set = tbl_merge, +} - return function(name, info, current, new) - return value_mutator( - name, - info, - convert_value_to_lua(name, info, current), - convert_value_to_lua(name, info, new), - methods - ) - end -end)() +--- Handles the '+' operator +local function add_value(info, current, new) + return add_methods[info.metatype]( + convert_value_to_lua(info, current), + convert_value_to_lua(info, new) + ) +end ---- Handles the '-' operator -local remove_value = (function() - local remove_one_item = function(t, val) - if vim.tbl_islist(t) then - local remove_index = nil - for i, v in ipairs(t) do - if v == val then - remove_index = i - end +local function remove_one_item(t, val) + if vim.tbl_islist(t) then + local remove_index = nil + for i, v in ipairs(t) do + if v == val then + remove_index = i end + end - if remove_index then - table.remove(t, remove_index) - end - else - t[val] = nil + if remove_index then + table.remove(t, remove_index) end + else + t[val] = nil end +end - local methods = { - [OptionTypes.NUMBER] = function(left, right) - return left - right - end, - - [OptionTypes.STRING] = function() - error('Subtraction not supported for strings.') - end, - - [OptionTypes.ARRAY] = function(left, right) - if type(right) == 'string' then - remove_one_item(left, right) - else - for _, v in ipairs(right) do - remove_one_item(left, v) - end - end +local remove_methods = { + number = function(left, right) + return left - right + end, - return left - end, + string = function() + error('Subtraction not supported for strings.') + end, - [OptionTypes.MAP] = function(left, right) - if type(right) == 'string' then - left[right] = nil - else - for _, v in ipairs(right) do - left[v] = nil - end + array = function(left, right) + if type(right) == 'string' then + remove_one_item(left, right) + else + for _, v in ipairs(right) do + remove_one_item(left, v) end + end - return left - end, - - [OptionTypes.SET] = function(left, right) - if type(right) == 'string' then - left[right] = nil - else - for _, v in ipairs(right) do - left[v] = nil - end - end + return left + end, - return left - end, - } + map = tbl_remove, + set = tbl_remove, +} - return function(name, info, current, new) - return value_mutator(name, info, convert_value_to_lua(name, info, current), new, methods) - end -end)() +--- Handles the '-' operator +local function remove_value(info, current, new) + return remove_methods[info.metatype](convert_value_to_lua(info, current), new) +end -local create_option_metatable = function(set_type) - local set_mt, option_mt +local function create_option_accessor(scope) + local option_mt - local make_option = function(name, value) - _setup() + local function make_option(name, value) local info = assert(options_info[name], 'Not a valid option name: ' .. name) if type(value) == 'table' and getmetatable(value) == option_mt then @@ -651,67 +483,58 @@ local create_option_metatable = function(set_type) }, option_mt) end - local scope - if set_type == SET_TYPES.GLOBAL then - scope = 'global' - elseif set_type == SET_TYPES.LOCAL then - scope = 'local' - end - option_mt = { -- To set a value, instead use: -- opt[my_option] = value _set = function(self) local value = convert_value_to_vim(self._name, self._info, self._value) a.nvim_set_option_value(self._name, value, { scope = scope }) - - return self end, get = function(self) - return convert_value_to_lua(self._name, self._info, self._value) + return convert_value_to_lua(self._info, self._value) end, append = function(self, right) - return self:__add(right):_set() + self._value = add_value(self._info, self._value, right) + self:_set() end, __add = function(self, right) - return make_option(self._name, add_value(self._name, self._info, self._value, right)) + return make_option(self._name, add_value(self._info, self._value, right)) end, prepend = function(self, right) - return self:__pow(right):_set() + self._value = prepend_value(self._info, self._value, right) + self:_set() end, __pow = function(self, right) - return make_option(self._name, prepend_value(self._name, self._info, self._value, right)) + return make_option(self._name, prepend_value(self._info, self._value, right)) end, remove = function(self, right) - return self:__sub(right):_set() + self._value = remove_value(self._info, self._value, right) + self:_set() end, __sub = function(self, right) - return make_option(self._name, remove_value(self._name, self._info, self._value, right)) + return make_option(self._name, remove_value(self._info, self._value, right)) end, } option_mt.__index = option_mt - set_mt = { + return setmetatable({}, { __index = function(_, k) return make_option(k, a.nvim_get_option_value(k, { scope = scope })) end, __newindex = function(_, k, v) - local opt = make_option(k, v) - opt:_set() + make_option(k, v):_set() end, - } - - return set_mt + }) end -vim.opt = setmetatable({}, create_option_metatable(SET_TYPES.SET)) -vim.opt_local = setmetatable({}, create_option_metatable(SET_TYPES.LOCAL)) -vim.opt_global = setmetatable({}, create_option_metatable(SET_TYPES.GLOBAL)) +vim.opt = create_option_accessor() +vim.opt_local = create_option_accessor('local') +vim.opt_global = create_option_accessor('global') |