aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
authorLewis Russell <lewis6991@gmail.com>2023-07-17 17:40:14 +0100
committerGitHub <noreply@github.com>2023-07-17 17:40:14 +0100
commit80cf0f3d29fa337d43ec417759cb061bd2798ea8 (patch)
tree16de8180f2fb9feefd0df94e0c8598475ec28324 /runtime/lua/vim
parent1b9ccd38a12f8fdbdff51ef0b3ff363540f745ec (diff)
parent6e9b204afbe5f16c44a2697aed07aafff36bf856 (diff)
downloadrneovim-80cf0f3d29fa337d43ec417759cb061bd2798ea8.tar.gz
rneovim-80cf0f3d29fa337d43ec417759cb061bd2798ea8.tar.bz2
rneovim-80cf0f3d29fa337d43ec417759cb061bd2798ea8.zip
Merge pull request #24363 from lewis6991/docs/luatypes
docs(lua): move some function docs to lua files
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/_editor.lua2
-rw-r--r--runtime/lua/vim/_meta.lua575
-rw-r--r--runtime/lua/vim/_meta/builtin.lua238
-rw-r--r--runtime/lua/vim/_meta/diff.lua69
-rw-r--r--runtime/lua/vim/_meta/json.lua37
-rw-r--r--runtime/lua/vim/_meta/misc.lua13
-rw-r--r--runtime/lua/vim/_meta/mpack.lua15
-rw-r--r--runtime/lua/vim/_meta/regex.lua36
-rw-r--r--runtime/lua/vim/_meta/spell.lua31
-rw-r--r--runtime/lua/vim/_options.lua928
-rw-r--r--runtime/lua/vim/highlight.lua65
-rw-r--r--runtime/lua/vim/iter.lua4
-rw-r--r--runtime/lua/vim/version.lua2
13 files changed, 1417 insertions, 598 deletions
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 47215416e0..bb0aa97a0d 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -1094,7 +1094,7 @@ function vim._init_defaults()
vim._init_default_autocmds()
end
-require('vim._meta')
+require('vim._options')
-- Remove at Nvim 1.0
---@deprecated
diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua
deleted file mode 100644
index 41e6e8be86..0000000000
--- a/runtime/lua/vim/_meta.lua
+++ /dev/null
@@ -1,575 +0,0 @@
-local api = vim.api
-
--- 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 'string'
- end
- return info.type
-end
-
-local options_info = setmetatable({}, {
- __index = function(t, k)
- local info = api.nvim_get_option_info(k)
- info.metatype = get_option_metatype(k, info)
- rawset(t, k, info)
- return rawget(t, k)
- end,
-})
-
-vim.env = setmetatable({}, {
- __index = function(_, k)
- local v = vim.fn.getenv(k)
- if v == vim.NIL then
- return nil
- end
- return v
- end,
-
- __newindex = function(_, k, v)
- vim.fn.setenv(k, v)
- end,
-})
-
-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
-end
-
-local function new_buf_opt_accessor(bufnr)
- return setmetatable({}, {
- __index = function(_, k)
- if bufnr == nil and type(k) == 'number' then
- return new_buf_opt_accessor(k)
- end
- opt_validate(k, 'buf')
- return api.nvim_get_option_value(k, { buf = bufnr or 0 })
- end,
-
- __newindex = function(_, k, v)
- opt_validate(k, 'buf')
- return api.nvim_set_option_value(k, v, { buf = bufnr or 0 })
- end,
- })
-end
-
-vim.bo = new_buf_opt_accessor()
-
-local function new_win_opt_accessor(winid, bufnr)
- return setmetatable({}, {
- __index = function(_, k)
- if bufnr == nil and type(k) == 'number' then
- if winid == nil then
- return new_win_opt_accessor(k)
- else
- return new_win_opt_accessor(winid, k)
- end
- end
-
- if bufnr ~= nil and bufnr ~= 0 then
- error('only bufnr=0 is supported')
- end
-
- opt_validate(k, 'win')
- -- TODO(lewis6991): allow passing both buf and win to nvim_get_option_value
- return api.nvim_get_option_value(k, {
- scope = bufnr and 'local' or nil,
- win = winid or 0,
- })
- end,
-
- __newindex = function(_, k, v)
- opt_validate(k, 'win')
- -- TODO(lewis6991): allow passing both buf and win to nvim_set_option_value
- return api.nvim_set_option_value(k, v, {
- scope = bufnr and 'local' or nil,
- win = winid or 0,
- })
- end,
- })
-end
-
-vim.wo = new_win_opt_accessor()
-
--- vim global option
--- this ONLY sets the global option. like `setglobal`
-vim.go = setmetatable({}, {
- __index = function(_, k)
- return api.nvim_get_option_value(k, { scope = 'global' })
- end,
- __newindex = function(_, k, v)
- return api.nvim_set_option_value(k, v, { scope = 'global' })
- end,
-})
-
--- vim `set` style options.
--- it has no additional metamethod magic.
-vim.o = setmetatable({}, {
- __index = function(_, k)
- return api.nvim_get_option_value(k, {})
- end,
- __newindex = function(_, k, v)
- return api.nvim_set_option_value(k, v, {})
- end,
-})
-
----@brief [[
---- vim.opt, vim.opt_local and vim.opt_global implementation
----
---- To be used as helpers for working with options within neovim.
---- For information on how to use, see :help vim.opt
----
----@brief ]]
-
---- Preserves the order and does not mutate the original list
-local function remove_duplicate_values(t)
- local result, seen = {}, {}
- for _, v in ipairs(t) do
- if not seen[v] then
- table.insert(result, v)
- end
-
- seen[v] = true
- end
-
- return result
-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)
- local type_of_value = type(value)
- for _, valid_type in ipairs(types) do
- if valid_type == type_of_value then
- return
- end
- end
-
- error(
- string.format(
- "Invalid option type '%s' for '%s', should be %s",
- type_of_value,
- name,
- table.concat(types, ' or ')
- )
- )
-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 = {
- boolean = { 'boolean' },
- number = { 'number' },
- string = { 'string' },
- set = { 'string', 'table' },
- array = { 'string', 'table' },
- map = { 'string', 'table' },
-}
-
--- 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,
-
- set = function(info, value)
- if type(value) == 'string' then
- return value
- 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
- 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
- end
-
- return result
- end
- 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,
-
- map = function(_, value)
- if type(value) == 'string' then
- return value
- end
-
- local result = {}
- for opt_key, opt_value in pairs(value) do
- table.insert(result, string.format('%s:%s', opt_key, opt_value))
- end
-
- table.sort(result)
- return table.concat(result, ',')
- end,
-}
-
---- 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
-
- assert_valid_value(name, value, valid_types[info.metatype])
-
- return to_vim_value[info.metatype](info, 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,
-
- array = function(info, value)
- if type(value) == 'table' then
- if not info.allows_duplicates then
- value = remove_duplicate_values(value)
- end
-
- return value
- end
-
- -- Empty strings mean that there is nothing there,
- -- so empty table should be returned.
- if value == '' then
- return {}
- end
-
- -- Handles unescaped commas in a list.
- if string.find(value, ',,,') then
- local left, right = unpack(vim.split(value, ',,,'))
-
- local result = {}
- vim.list_extend(result, vim.split(left, ','))
- table.insert(result, ',')
- vim.list_extend(result, vim.split(right, ','))
-
- table.sort(result)
-
- return result
- end
-
- if string.find(value, ',^,,', 1, true) then
- local left, right = unpack(vim.split(value, ',^,,', true))
-
- local result = {}
- vim.list_extend(result, vim.split(left, ','))
- table.insert(result, '^,')
- vim.list_extend(result, vim.split(right, ','))
-
- table.sort(result)
-
- return result
- end
-
- return vim.split(value, ',')
- end,
-
- set = function(info, value)
- if type(value) == 'table' then
- return value
- end
-
- -- Empty strings mean that there is nothing there,
- -- so empty table should be returned.
- if value == '' then
- return {}
- end
-
- assert(info.flaglist, 'That is the only one I know how to handle')
-
- 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
-
- return result
- else
- local result = {}
- for i = 1, #value do
- result[value:sub(i, i)] = true
- end
-
- return result
- end
- end,
-
- map = function(info, raw_value)
- if type(raw_value) == 'table' then
- return raw_value
- end
-
- assert(info.commalist, 'Only commas are supported currently')
-
- local result = {}
-
- 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)
-
- result[key] = value
- end
-
- return result
- 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
-
-local prepend_methods = {
- number = function()
- error("The '^' operator is not currently supported for")
- end,
-
- string = function(left, right)
- return right .. left
- end,
-
- array = function(left, right)
- for i = #right, 1, -1 do
- table.insert(left, 1, right[i])
- end
-
- return left
- end,
-
- map = tbl_merge,
- set = tbl_merge,
-}
-
---- 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
-
-local add_methods = {
- number = function(left, right)
- return left + right
- end,
-
- string = function(left, right)
- return left .. right
- end,
-
- array = function(left, right)
- for _, v in ipairs(right) do
- table.insert(left, v)
- end
-
- return left
- end,
-
- map = tbl_merge,
- set = tbl_merge,
-}
-
---- 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
-
-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
- end
-end
-
-local remove_methods = {
- number = function(left, right)
- return left - right
- end,
-
- string = function()
- error('Subtraction not supported for strings.')
- 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,
-
- map = tbl_remove,
- set = tbl_remove,
-}
-
---- Handles the '-' operator
-local function remove_value(info, current, new)
- return remove_methods[info.metatype](convert_value_to_lua(info, current), new)
-end
-
-local function create_option_accessor(scope)
- local option_mt
-
- 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
- assert(name == value._name, "must be the same value, otherwise that's weird.")
-
- value = value._value
- end
-
- return setmetatable({
- _name = name,
- _value = value,
- _info = info,
- }, option_mt)
- 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)
- api.nvim_set_option_value(self._name, value, { scope = scope })
- end,
-
- get = function(self)
- return convert_value_to_lua(self._info, self._value)
- end,
-
- append = function(self, right)
- self._value = add_value(self._info, self._value, right)
- self:_set()
- end,
-
- __add = function(self, right)
- return make_option(self._name, add_value(self._info, self._value, right))
- end,
-
- prepend = function(self, right)
- self._value = prepend_value(self._info, self._value, right)
- self:_set()
- end,
-
- __pow = function(self, right)
- return make_option(self._name, prepend_value(self._info, self._value, right))
- end,
-
- remove = function(self, right)
- self._value = remove_value(self._info, self._value, right)
- self:_set()
- end,
-
- __sub = function(self, right)
- return make_option(self._name, remove_value(self._info, self._value, right))
- end,
- }
- option_mt.__index = option_mt
-
- return setmetatable({}, {
- __index = function(_, k)
- return make_option(k, api.nvim_get_option_value(k, {}))
- end,
-
- __newindex = function(_, k, v)
- make_option(k, v):_set()
- end,
- })
-end
-
-vim.opt = create_option_accessor()
-vim.opt_local = create_option_accessor('local')
-vim.opt_global = create_option_accessor('global')
diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua
new file mode 100644
index 0000000000..980e40d61b
--- /dev/null
+++ b/runtime/lua/vim/_meta/builtin.lua
@@ -0,0 +1,238 @@
+---@meta
+
+-- luacheck: no unused args
+
+---@defgroup vim.builtin
+---
+---@brief <pre>help
+---vim.api.{func}({...}) *vim.api*
+--- Invokes Nvim |API| function {func} with arguments {...}.
+--- Example: call the "nvim_get_current_line()" API function: >lua
+--- print(tostring(vim.api.nvim_get_current_line()))
+---
+---vim.NIL *vim.NIL*
+--- Special value representing NIL in |RPC| and |v:null| in Vimscript
+--- conversion, and similar cases. Lua `nil` cannot be used as part of a Lua
+--- table representing a Dictionary or Array, because it is treated as
+--- missing: `{"foo", nil}` is the same as `{"foo"}`.
+---
+---vim.type_idx *vim.type_idx*
+--- Type index for use in |lua-special-tbl|. Specifying one of the values from
+--- |vim.types| allows typing the empty table (it is unclear whether empty Lua
+--- table represents empty list or empty array) and forcing integral numbers
+--- to be |Float|. See |lua-special-tbl| for more details.
+---
+---vim.val_idx *vim.val_idx*
+--- Value index for tables representing |Float|s. A table representing
+--- floating-point value 1.0 looks like this: >lua
+--- {
+--- [vim.type_idx] = vim.types.float,
+--- [vim.val_idx] = 1.0,
+--- }
+---< See also |vim.type_idx| and |lua-special-tbl|.
+---
+---vim.types *vim.types*
+--- Table with possible values for |vim.type_idx|. Contains two sets of
+--- key-value pairs: first maps possible values for |vim.type_idx| to
+--- human-readable strings, second maps human-readable type names to values
+--- for |vim.type_idx|. Currently contains pairs for `float`, `array` and
+--- `dictionary` types.
+---
+--- Note: One must expect that values corresponding to `vim.types.float`,
+--- `vim.types.array` and `vim.types.dictionary` fall under only two following
+--- assumptions:
+--- 1. Value may serve both as a key and as a value in a table. Given the
+--- properties of Lua tables this basically means “value is not `nil`”.
+--- 2. For each value in `vim.types` table `vim.types[vim.types[value]]` is the
+--- same as `value`.
+--- No other restrictions are put on types, and it is not guaranteed that
+--- values corresponding to `vim.types.float`, `vim.types.array` and
+--- `vim.types.dictionary` will not change or that `vim.types` table will only
+--- contain values for these three types.
+---
+--- *log_levels* *vim.log.levels*
+---Log levels are one of the values defined in `vim.log.levels`:
+---
+--- vim.log.levels.DEBUG
+--- vim.log.levels.ERROR
+--- vim.log.levels.INFO
+--- vim.log.levels.TRACE
+--- vim.log.levels.WARN
+--- vim.log.levels.OFF
+---
+---</pre>
+
+--- Returns true if the code is executing as part of a "fast" event handler,
+--- where most of the API is disabled. These are low-level events (e.g.
+--- |lua-loop-callbacks|) which can be invoked whenever Nvim polls for input.
+--- When this is `false` most API functions are callable (but may be subject
+--- to other restrictions such as |textlock|).
+function vim.in_fast_event() end
+
+--- Creates a special empty table (marked with a metatable), which Nvim to an
+--- empty dictionary when translating Lua values to Vimscript or API types.
+--- Nvim by default converts an empty table `{}` without this metatable to an
+--- list/array.
+---
+--- Note: If numeric keys are present in the table, Nvim ignores the metatable
+--- marker and converts the dict to a list/array anyway.
+function vim.empty_dict() end
+
+--- Sends {event} to {channel} via |RPC| and returns immediately. If {channel}
+--- is 0, the event is broadcast to all channels.
+---
+--- This function also works in a fast callback |lua-loop-callbacks|.
+--- @param channel integer
+--- @param method string
+--- @param args? any[]
+--- @param ...? any
+function vim.rpcnotify(channel, method, args, ...) end
+
+--- Sends a request to {channel} to invoke {method} via |RPC| and blocks until
+--- a response is received.
+---
+--- Note: NIL values as part of the return value is represented as |vim.NIL|
+--- special value
+--- @param channel integer
+--- @param method string
+--- @param args? any[]
+--- @param ...? any
+function vim.rpcrequest(channel, method, args, ...) end
+
+--- Compares strings case-insensitively.
+--- @param a string
+--- @param b string
+--- @return 0|1|-1
+--- if strings are
+--- equal, {a} is greater than {b} or {a} is lesser than {b}, respectively.
+function vim.stricmp(a, b) end
+
+--- Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not
+--- supplied, it defaults to false (use UTF-32). Returns the byte index.
+---
+--- Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|.
+--- An {index} in the middle of a UTF-16 sequence is rounded upwards to
+--- the end of that sequence.
+--- @param str string
+--- @param index number
+--- @param use_utf16? any
+function vim.str_byteindex(str, index, use_utf16) end
+
+--- Convert byte index to UTF-32 and UTF-16 indices. If {index} is not
+--- supplied, the length of the string is used. All indices are zero-based.
+---
+--- Embedded NUL bytes are treated as terminating the string. Invalid UTF-8
+--- bytes, and embedded surrogates are counted as one code point each. An
+--- {index} in the middle of a UTF-8 sequence is rounded upwards to the end of
+--- that sequence.
+--- @param str string
+--- @param index? number
+--- @return integer UTF-32 index
+--- @return integer UTF-16 index
+function vim.str_utfindex(str, index) end
+
+--- The result is a String, which is the text {str} converted from
+--- encoding {from} to encoding {to}. When the conversion fails `nil` is
+--- returned. When some characters could not be converted they
+--- are replaced with "?".
+--- The encoding names are whatever the iconv() library function
+--- can accept, see ":Man 3 iconv".
+---
+--- @param str string Text to convert
+--- @param from number Encoding of {str}
+--- @param to number Target encoding
+--- @param opts? table<string,any>
+--- @return string|nil Converted string if conversion succeeds, `nil` otherwise.
+function vim.iconv(str, from, to, opts) end
+
+--- Schedules {callback} to be invoked soon by the main event-loop. Useful
+--- to avoid |textlock| or other temporary restrictions.
+--- @param callback fun()
+function vim.schedule(callback) end
+
+--- Wait for {time} in milliseconds until {callback} returns `true`.
+---
+--- Executes {callback} immediately and at approximately {interval}
+--- milliseconds (default 200). Nvim still processes other events during
+--- this time.
+---
+--- Examples:
+--- <pre>lua
+---
+--- ---
+--- -- Wait for 100 ms, allowing other events to process
+--- vim.wait(100, function() end)
+---
+--- ---
+--- -- Wait for 100 ms or until global variable set.
+--- vim.wait(100, function() return vim.g.waiting_for_var end)
+---
+--- ---
+--- -- Wait for 1 second or until global variable set, checking every ~500 ms
+--- vim.wait(1000, function() return vim.g.waiting_for_var end, 500)
+---
+--- ---
+--- -- Schedule a function to set a value in 100ms
+--- vim.defer_fn(function() vim.g.timer_result = true end, 100)
+---
+--- -- Would wait ten seconds if results blocked. Actually only waits 100 ms
+--- if vim.wait(10000, function() return vim.g.timer_result end) then
+--- print('Only waiting a little bit of time!')
+--- end
+--- </pre>
+---
+--- @param time integer Number of milliseconds to wait
+--- @param callback? fun(): boolean Optional callback. Waits until {callback} returns true
+--- @param interval? integer (Approximate) number of milliseconds to wait between polls
+--- @param fast_only? boolean If true, only |api-fast| events will be processed.
+--- If called from while in an |api-fast| event, will
+--- automatically be set to `true`.
+--- @return boolean, nil|-1|-2
+--- - If {callback} returns `true` during the {time}: `true, nil`
+--- - If {callback} never returns `true` during the {time}: `false, -1`
+--- - If {callback} is interrupted during the {time}: `false, -2`
+--- - If {callback} errors, the error is raised.
+function vim.wait(time, callback, interval, fast_only) end
+
+--- Attach to ui events, similar to |nvim_ui_attach()| but receive events
+--- as Lua callback. Can be used to implement screen elements like
+--- popupmenu or message handling in Lua.
+---
+--- {options} should be a dictionary-like table, where `ext_...` options should
+--- be set to true to receive events for the respective external element.
+---
+--- {callback} receives event name plus additional parameters. See |ui-popupmenu|
+--- and the sections below for event format for respective events.
+---
+--- WARNING: This api is considered experimental. Usability will vary for
+--- different screen elements. In particular `ext_messages` behavior is subject
+--- to further changes and usability improvements. This is expected to be
+--- used to handle messages when setting 'cmdheight' to zero (which is
+--- likewise experimental).
+---
+--- Example (stub for a |ui-popupmenu| implementation):
+--- <pre>lua
+---
+--- ns = vim.api.nvim_create_namespace('my_fancy_pum')
+---
+--- vim.ui_attach(ns, {ext_popupmenu=true}, function(event, ...)
+--- if event == "popupmenu_show" then
+--- local items, selected, row, col, grid = ...
+--- print("display pum ", #items)
+--- elseif event == "popupmenu_select" then
+--- local selected = ...
+--- print("selected", selected)
+--- elseif event == "popupmenu_hide" then
+--- print("FIN")
+--- end
+--- end)
+--- </pre>
+--- @param ns integer
+--- @param options table<string, any>
+--- @param callback fun()
+function vim.ui_attach(ns, options, callback) end
+
+--- Detach a callback previously attached with |vim.ui_attach()| for the
+--- given namespace {ns}.
+--- @param ns integer
+function vim.ui_detach(ns) end
diff --git a/runtime/lua/vim/_meta/diff.lua b/runtime/lua/vim/_meta/diff.lua
new file mode 100644
index 0000000000..246ac0c75a
--- /dev/null
+++ b/runtime/lua/vim/_meta/diff.lua
@@ -0,0 +1,69 @@
+---@meta
+
+-- luacheck: no unused args
+
+--- Run diff on strings {a} and {b}. Any indices returned by this function,
+--- either directly or via callback arguments, are 1-based.
+---
+--- Examples:
+--- <pre>lua
+--- vim.diff('a\\n', 'b\\nc\\n')
+--- -- =>
+--- -- @@ -1 +1,2 @@
+--- -- -a
+--- -- +b
+--- -- +c
+---
+--- vim.diff('a\\n', 'b\\nc\\n', {result_type = 'indices'})
+--- -- =>
+--- -- {
+--- -- {1, 1, 1, 2}
+--- -- }
+--- </pre>
+---
+--- @param a string First string to compare
+--- @param b string Second string to compare
+--- @param opts table<string,any> Optional parameters:
+--- - `on_hunk` (callback):
+--- Invoked for each hunk in the diff. Return a negative number
+--- to cancel the callback for any remaining hunks.
+--- Args:
+--- - `start_a` (integer): Start line of hunk in {a}.
+--- - `count_a` (integer): Hunk size in {a}.
+--- - `start_b` (integer): Start line of hunk in {b}.
+--- - `count_b` (integer): Hunk size in {b}.
+--- - `result_type` (string): Form of the returned diff:
+--- - "unified": (default) String in unified format.
+--- - "indices": Array of hunk locations.
+--- Note: This option is ignored if `on_hunk` is used.
+--- - `linematch` (boolean|integer): Run linematch on the resulting hunks
+--- from xdiff. When integer, only hunks upto this size in
+--- lines are run through linematch. Requires `result_type = indices`,
+--- ignored otherwise.
+--- - `algorithm` (string):
+--- Diff algorithm to use. Values:
+--- - "myers" the default algorithm
+--- - "minimal" spend extra time to generate the
+--- smallest possible diff
+--- - "patience" patience diff algorithm
+--- - "histogram" histogram diff algorithm
+--- - `ctxlen` (integer): Context length
+--- - `interhunkctxlen` (integer):
+--- Inter hunk context length
+--- - `ignore_whitespace` (boolean):
+--- Ignore whitespace
+--- - `ignore_whitespace_change` (boolean):
+--- Ignore whitespace change
+--- - `ignore_whitespace_change_at_eol` (boolean)
+--- Ignore whitespace change at end-of-line.
+--- - `ignore_cr_at_eol` (boolean)
+--- Ignore carriage return at end-of-line
+--- - `ignore_blank_lines` (boolean)
+--- Ignore blank lines
+--- - `indent_heuristic` (boolean):
+--- Use the indent heuristic for the internal
+--- diff library.
+---
+--- @return string|table|nil
+--- See {opts.result_type}. `nil` if {opts.on_hunk} is given.
+function vim.diff(a, b, opts) end
diff --git a/runtime/lua/vim/_meta/json.lua b/runtime/lua/vim/_meta/json.lua
new file mode 100644
index 0000000000..76a6c7b733
--- /dev/null
+++ b/runtime/lua/vim/_meta/json.lua
@@ -0,0 +1,37 @@
+--- @meta
+
+-- luacheck: no unused args
+
+--- @defgroup vim.json
+---
+--- This module provides encoding and decoding of Lua objects to and
+--- from JSON-encoded strings. Supports |vim.NIL| and |vim.empty_dict()|.
+
+--- Decodes (or "unpacks") the JSON-encoded {str} to a Lua object.
+---
+--- - Decodes JSON "null" as |vim.NIL| (controllable by {opts}, see below).
+--- - Decodes empty object as |vim.empty_dict()|.
+--- - Decodes empty array as `{}` (empty Lua table).
+---
+--- Example:
+--- <pre>lua
+--- :lua vim.print(vim.json.decode('{"bar":[],"foo":{},"zub":null}'))
+--- --> { bar = {}, foo = vim.empty_dict(), zub = vim.NIL }
+--- </pre>
+--- Parameters: ~
+--- • {str} Stringified JSON data.
+--- • {opts} Options map keys:
+--- • luanil: { object: bool, array: bool }
+--- • `luanil.object=true` converts `null` in JSON objects to
+--- Lua `nil` instead of `vim.NIL`.
+--- • `luanil.array=true` converts `null` in JSON arrays to Lua
+--- `nil` instead of `vim.NIL`.
+--- @param str string
+--- @param opts? table<string, any>
+--- @return any
+function vim.json.decode(str, opts) end
+
+--- Encodes (or "packs") Lua object {obj} as JSON in a Lua string.
+--- @param obj any
+--- @return string
+function vim.json.encode(obj) end
diff --git a/runtime/lua/vim/_meta/misc.lua b/runtime/lua/vim/_meta/misc.lua
new file mode 100644
index 0000000000..8a76755962
--- /dev/null
+++ b/runtime/lua/vim/_meta/misc.lua
@@ -0,0 +1,13 @@
+---@meta
+
+-- luacheck: no unused args
+
+--- Invokes |vim-function| or |user-function| {func} with arguments {...}.
+--- See also |vim.fn|.
+--- Equivalent to:
+--- <pre>lua
+--- vim.fn[func]({...})
+--- </pre>
+--- @param func fun()
+--- @param ... any
+function vim.call(func, ...) end
diff --git a/runtime/lua/vim/_meta/mpack.lua b/runtime/lua/vim/_meta/mpack.lua
new file mode 100644
index 0000000000..54e097ad97
--- /dev/null
+++ b/runtime/lua/vim/_meta/mpack.lua
@@ -0,0 +1,15 @@
+--- @meta
+
+-- luacheck: no unused args
+
+--- @defgroup vim.mpack
+---
+--- This module provides encoding and decoding of Lua objects to and
+--- from msgpack-encoded strings. Supports |vim.NIL| and |vim.empty_dict()|.
+
+--- Decodes (or "unpacks") the msgpack-encoded {str} to a Lua object.
+--- @param str string
+function vim.mpack.decode(str) end
+
+--- Encodes (or "packs") Lua object {obj} as msgpack in a Lua string.
+function vim.mpack.encode(obj) end
diff --git a/runtime/lua/vim/_meta/regex.lua b/runtime/lua/vim/_meta/regex.lua
new file mode 100644
index 0000000000..4bca67797a
--- /dev/null
+++ b/runtime/lua/vim/_meta/regex.lua
@@ -0,0 +1,36 @@
+--- @meta
+
+-- luacheck: no unused args
+
+--- @defgroup vim.regex
+---
+--- @brief Vim regexes can be used directly from Lua. Currently they only allow
+--- matching within a single line.
+
+--- Parse the Vim regex {re} and return a regex object. Regexes are "magic"
+--- and case-sensitive by default, regardless of 'magic' and 'ignorecase'.
+--- They can be controlled with flags, see |/magic| and |/ignorecase|.
+--- @param re string
+--- @return vim.regex
+function vim.regex(re) end
+
+--- @class vim.regex
+local regex = {} -- luacheck: no unused
+
+--- Match the string against the regex. If the string should match the regex
+--- precisely, surround the regex with `^` and `$`. If the was a match, the
+--- byte indices for the beginning and end of the match is returned. When
+--- there is no match, `nil` is returned. As any integer is truth-y,
+--- `regex:match()` can be directly used as a condition in an if-statement.
+--- @param str string
+function regex:match_str(str) end
+
+--- Match line {line_idx} (zero-based) in buffer {bufnr}. If {start} and {end}
+--- are supplied, match only this byte index range. Otherwise see
+--- |regex:match_str()|. If {start} is used, then the returned byte indices
+--- will be relative {start}.
+--- @param bufnr integer
+--- @param line_idx integer
+--- @param start? integer
+--- @param end_? integer
+function regex:match_line(bufnr, line_idx, start, end_) end
diff --git a/runtime/lua/vim/_meta/spell.lua b/runtime/lua/vim/_meta/spell.lua
new file mode 100644
index 0000000000..d55867f769
--- /dev/null
+++ b/runtime/lua/vim/_meta/spell.lua
@@ -0,0 +1,31 @@
+--- @meta
+
+-- luacheck: no unused args
+
+--- Check {str} for spelling errors. Similar to the Vimscript function
+--- |spellbadword()|.
+---
+--- Note: The behaviour of this function is dependent on: 'spelllang',
+--- 'spellfile', 'spellcapcheck' and 'spelloptions' which can all be local to
+--- the buffer. Consider calling this with |nvim_buf_call()|.
+---
+--- Example:
+--- <pre>lua
+--- vim.spell.check("the quik brown fox")
+--- -- =>
+--- -- {
+--- -- {'quik', 'bad', 5}
+--- -- }
+--- </pre>
+---
+--- @param str string
+--- @return {[1]: string, [2]: string, [3]: string}[]
+--- List of tuples with three items:
+--- - The badly spelled word.
+--- - The type of the spelling error:
+--- "bad" spelling mistake
+--- "rare" rare word
+--- "local" word only valid in another region
+--- "caps" word should start with Capital
+--- - The position in {str} where the word begins.
+function vim.spell.check(str) end
diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua
new file mode 100644
index 0000000000..6dbe4cf64a
--- /dev/null
+++ b/runtime/lua/vim/_options.lua
@@ -0,0 +1,928 @@
+---@defgroup lua-vimscript
+---
+---@brief Nvim Lua provides an interface or "bridge" to Vimscript variables and
+---functions, and editor commands and options.
+---
+---Objects passed over this bridge are COPIED (marshalled): there are no
+---"references". |lua-guide-variables| For example, using \`vim.fn.remove()\` on
+---a Lua list copies the list object to Vimscript and does NOT modify the Lua
+---list:
+---<pre>lua
+--- local list = { 1, 2, 3 }
+--- vim.fn.remove(list, 0)
+--- vim.print(list) --> "{ 1, 2, 3 }"
+---</pre>
+
+---@addtogroup lua-vimscript
+---@brief <pre>help
+---vim.call({func}, {...}) *vim.call()*
+--- Invokes |vim-function| or |user-function| {func} with arguments {...}.
+--- See also |vim.fn|.
+--- Equivalent to: >lua
+--- vim.fn[func]({...})
+---<
+---vim.cmd({command})
+--- See |vim.cmd()|.
+---
+---vim.fn.{func}({...}) *vim.fn*
+--- Invokes |vim-function| or |user-function| {func} with arguments {...}.
+--- To call autoload functions, use the syntax: >lua
+--- vim.fn['some\#function']({...})
+---<
+--- Unlike vim.api.|nvim_call_function()| this converts directly between Vim
+--- objects and Lua objects. If the Vim function returns a float, it will be
+--- represented directly as a Lua number. Empty lists and dictionaries both
+--- are represented by an empty table.
+---
+--- Note: |v:null| values as part of the return value is represented as
+--- |vim.NIL| special value
+---
+--- Note: vim.fn keys are generated lazily, thus `pairs(vim.fn)` only
+--- enumerates functions that were called at least once.
+---
+--- Note: The majority of functions cannot run in |api-fast| callbacks with some
+--- undocumented exceptions which are allowed.
+---
+--- *lua-vim-variables*
+---The Vim editor global dictionaries |g:| |w:| |b:| |t:| |v:| can be accessed
+---from Lua conveniently and idiomatically by referencing the `vim.*` Lua tables
+---described below. In this way you can easily read and modify global Vimscript
+---variables from Lua.
+---
+---Example: >lua
+---
+--- vim.g.foo = 5 -- Set the g:foo Vimscript variable.
+--- print(vim.g.foo) -- Get and print the g:foo Vimscript variable.
+--- vim.g.foo = nil -- Delete (:unlet) the Vimscript variable.
+--- vim.b[2].foo = 6 -- Set b:foo for buffer 2
+---<
+---
+---Note that setting dictionary fields directly will not write them back into
+---Nvim. This is because the index into the namespace simply returns a copy.
+---Instead the whole dictionary must be written as one. This can be achieved by
+---creating a short-lived temporary.
+---
+---Example: >lua
+---
+--- vim.g.my_dict.field1 = 'value' -- Does not work
+---
+--- local my_dict = vim.g.my_dict --
+--- my_dict.field1 = 'value' -- Instead do
+--- vim.g.my_dict = my_dict --
+---
+---vim.g *vim.g*
+--- Global (|g:|) editor variables.
+--- Key with no value returns `nil`.
+---
+---vim.b *vim.b*
+--- Buffer-scoped (|b:|) variables for the current buffer.
+--- Invalid or unset key returns `nil`. Can be indexed with
+--- an integer to access variables for a specific buffer.
+---
+---vim.w *vim.w*
+--- Window-scoped (|w:|) variables for the current window.
+--- Invalid or unset key returns `nil`. Can be indexed with
+--- an integer to access variables for a specific window.
+---
+---vim.t *vim.t*
+--- Tabpage-scoped (|t:|) variables for the current tabpage.
+--- Invalid or unset key returns `nil`. Can be indexed with
+--- an integer to access variables for a specific tabpage.
+---
+---vim.v *vim.v*
+--- |v:| variables.
+--- Invalid or unset key returns `nil`.
+---
+---vim.env *vim.env*
+--- Environment variables defined in the editor session.
+--- See |expand-env| and |:let-environment| for the Vimscript behavior.
+--- Invalid or unset key returns `nil`.
+--- Example: >lua
+--- vim.env.FOO = 'bar'
+--- print(vim.env.TERM)
+---<
+---</pre>
+
+local api = vim.api
+
+-- 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
+---@private
+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 'string'
+ end
+ return info.type
+end
+
+local options_info = setmetatable({}, {
+ __index = function(t, k)
+ local info = api.nvim_get_option_info(k)
+ info.metatype = get_option_metatype(k, info)
+ rawset(t, k, info)
+ return rawget(t, k)
+ end,
+})
+
+vim.env = setmetatable({}, {
+ __index = function(_, k)
+ local v = vim.fn.getenv(k)
+ if v == vim.NIL then
+ return nil
+ end
+ return v
+ end,
+
+ __newindex = function(_, k, v)
+ vim.fn.setenv(k, v)
+ end,
+})
+
+---@private
+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
+end
+
+---@private
+local function new_buf_opt_accessor(bufnr)
+ return setmetatable({}, {
+ __index = function(_, k)
+ if bufnr == nil and type(k) == 'number' then
+ return new_buf_opt_accessor(k)
+ end
+ opt_validate(k, 'buf')
+ return api.nvim_get_option_value(k, { buf = bufnr or 0 })
+ end,
+
+ __newindex = function(_, k, v)
+ opt_validate(k, 'buf')
+ return api.nvim_set_option_value(k, v, { buf = bufnr or 0 })
+ end,
+ })
+end
+
+---@private
+local function new_win_opt_accessor(winid, bufnr)
+ return setmetatable({}, {
+ __index = function(_, k)
+ if bufnr == nil and type(k) == 'number' then
+ if winid == nil then
+ return new_win_opt_accessor(k)
+ else
+ return new_win_opt_accessor(winid, k)
+ end
+ end
+
+ if bufnr ~= nil and bufnr ~= 0 then
+ error('only bufnr=0 is supported')
+ end
+
+ opt_validate(k, 'win')
+ -- TODO(lewis6991): allow passing both buf and win to nvim_get_option_value
+ return api.nvim_get_option_value(k, {
+ scope = bufnr and 'local' or nil,
+ win = winid or 0,
+ })
+ end,
+
+ __newindex = function(_, k, v)
+ opt_validate(k, 'win')
+ -- TODO(lewis6991): allow passing both buf and win to nvim_set_option_value
+ return api.nvim_set_option_value(k, v, {
+ scope = bufnr and 'local' or nil,
+ win = winid or 0,
+ })
+ end,
+ })
+end
+
+---@addtogroup lua-vimscript
+---@brief <pre>help
+---` ` *lua-options*
+--- *lua-vim-options*
+--- *lua-vim-set*
+--- *lua-vim-setlocal*
+---
+---Vim options can be accessed through |vim.o|, which behaves like Vimscript
+---|:set|.
+---
+--- Examples: ~
+---
+--- To set a boolean toggle:
+--- Vimscript: `set number`
+--- Lua: `vim.o.number = true`
+---
+--- To set a string value:
+--- Vimscript: `set wildignore=*.o,*.a,__pycache__`
+--- Lua: `vim.o.wildignore = '*.o,*.a,__pycache__'`
+---
+---Similarly, there is |vim.bo| and |vim.wo| for setting buffer-scoped and
+---window-scoped options. Note that this must NOT be confused with
+---|local-options| and |:setlocal|. There is also |vim.go| that only accesses the
+---global value of a |global-local| option, see |:setglobal|.
+---</pre>
+
+---@addtogroup lua-vimscript
+---@brief <pre>help
+---vim.o *vim.o*
+--- Get or set |options|. Like `:set`. Invalid key is an error.
+---
+--- Note: this works on both buffer-scoped and window-scoped options using the
+--- current buffer and window.
+---
+--- Example: >lua
+--- vim.o.cmdheight = 4
+--- print(vim.o.columns)
+--- print(vim.o.foo) -- error: invalid key
+---<
+---</pre>
+vim.o = setmetatable({}, {
+ __index = function(_, k)
+ return api.nvim_get_option_value(k, {})
+ end,
+ __newindex = function(_, k, v)
+ return api.nvim_set_option_value(k, v, {})
+ end,
+})
+
+---@addtogroup lua-vimscript
+---@brief <pre>help
+---vim.go *vim.go*
+--- Get or set global |options|. Like `:setglobal`. Invalid key is
+--- an error.
+---
+--- Note: this is different from |vim.o| because this accesses the global
+--- option value and thus is mostly useful for use with |global-local|
+--- options.
+---
+--- Example: >lua
+--- vim.go.cmdheight = 4
+--- print(vim.go.columns)
+--- print(vim.go.bar) -- error: invalid key
+---<
+---</pre>
+vim.go = setmetatable({}, {
+ __index = function(_, k)
+ return api.nvim_get_option_value(k, { scope = 'global' })
+ end,
+ __newindex = function(_, k, v)
+ return api.nvim_set_option_value(k, v, { scope = 'global' })
+ end,
+})
+
+---@addtogroup lua-vimscript
+---@brief <pre>help
+---vim.bo[{bufnr}] *vim.bo*
+--- Get or set buffer-scoped |options| for the buffer with number {bufnr}.
+--- Like `:set` and `:setlocal`. If [{bufnr}] is omitted then the current
+--- buffer is used. Invalid {bufnr} or key is an error.
+---
+--- Note: this is equivalent to both `:set` and `:setlocal`.
+---
+--- Example: >lua
+--- local bufnr = vim.api.nvim_get_current_buf()
+--- vim.bo[bufnr].buflisted = true -- same as vim.bo.buflisted = true
+--- print(vim.bo.comments)
+--- print(vim.bo.baz) -- error: invalid key
+---</pre>
+vim.bo = new_buf_opt_accessor()
+
+---@addtogroup lua-vimscript
+---@brief <pre>help
+---vim.wo[{winid}][{bufnr}] *vim.wo*
+--- Get or set window-scoped |options| for the window with handle {winid} and
+--- buffer with number {bufnr}. Like `:setlocal` if {bufnr} is provided, like
+--- `:set` otherwise. If [{winid}] is omitted then the current window is
+--- used. Invalid {winid}, {bufnr} or key is an error.
+---
+--- Note: only {bufnr} with value `0` (the current buffer in the window) is
+--- supported.
+---
+--- Example: >lua
+--- local winid = vim.api.nvim_get_current_win()
+--- vim.wo[winid].number = true -- same as vim.wo.number = true
+--- print(vim.wo.foldmarker)
+--- print(vim.wo.quux) -- error: invalid key
+--- vim.wo[winid][0].spell = false -- like ':setlocal nospell'
+---<
+---</pre>
+vim.wo = new_win_opt_accessor()
+
+---@brief [[
+--- vim.opt, vim.opt_local and vim.opt_global implementation
+---
+--- To be used as helpers for working with options within neovim.
+--- For information on how to use, see :help vim.opt
+---
+---@brief ]]
+
+--- Preserves the order and does not mutate the original list
+--- @private
+local function remove_duplicate_values(t)
+ local result, seen = {}, {}
+ for _, v in ipairs(t) do
+ if not seen[v] then
+ table.insert(result, v)
+ end
+
+ seen[v] = true
+ end
+
+ return result
+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.
+--- @private
+local function assert_valid_value(name, value, types)
+ local type_of_value = type(value)
+ for _, valid_type in ipairs(types) do
+ if valid_type == type_of_value then
+ return
+ end
+ end
+
+ error(
+ string.format(
+ "Invalid option type '%s' for '%s', should be %s",
+ type_of_value,
+ name,
+ table.concat(types, ' or ')
+ )
+ )
+end
+
+--- @private
+local function passthrough(_, x)
+ return x
+end
+
+--- @private
+local function tbl_merge(left, right)
+ return vim.tbl_extend('force', left, right)
+end
+
+--- @private
+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 = {
+ boolean = { 'boolean' },
+ number = { 'number' },
+ string = { 'string' },
+ set = { 'string', 'table' },
+ array = { 'string', 'table' },
+ map = { 'string', 'table' },
+}
+
+-- 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,
+
+ set = function(info, value)
+ if type(value) == 'string' then
+ return value
+ 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
+ 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
+ end
+
+ return result
+ end
+ 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,
+
+ map = function(_, value)
+ if type(value) == 'string' then
+ return value
+ end
+
+ local result = {}
+ for opt_key, opt_value in pairs(value) do
+ table.insert(result, string.format('%s:%s', opt_key, opt_value))
+ end
+
+ table.sort(result)
+ return table.concat(result, ',')
+ end,
+}
+
+--- Convert a Lua value to a vimoption_T value
+--- @private
+local function convert_value_to_vim(name, info, value)
+ if value == nil then
+ return vim.NIL
+ end
+
+ assert_valid_value(name, value, valid_types[info.metatype])
+
+ return to_vim_value[info.metatype](info, 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,
+
+ array = function(info, value)
+ if type(value) == 'table' then
+ if not info.allows_duplicates then
+ value = remove_duplicate_values(value)
+ end
+
+ return value
+ end
+
+ -- Empty strings mean that there is nothing there,
+ -- so empty table should be returned.
+ if value == '' then
+ return {}
+ end
+
+ -- Handles unescaped commas in a list.
+ if string.find(value, ',,,') then
+ local left, right = unpack(vim.split(value, ',,,'))
+
+ local result = {}
+ vim.list_extend(result, vim.split(left, ','))
+ table.insert(result, ',')
+ vim.list_extend(result, vim.split(right, ','))
+
+ table.sort(result)
+
+ return result
+ end
+
+ if string.find(value, ',^,,', 1, true) then
+ local left, right = unpack(vim.split(value, ',^,,', true))
+
+ local result = {}
+ vim.list_extend(result, vim.split(left, ','))
+ table.insert(result, '^,')
+ vim.list_extend(result, vim.split(right, ','))
+
+ table.sort(result)
+
+ return result
+ end
+
+ return vim.split(value, ',')
+ end,
+
+ set = function(info, value)
+ if type(value) == 'table' then
+ return value
+ end
+
+ -- Empty strings mean that there is nothing there,
+ -- so empty table should be returned.
+ if value == '' then
+ return {}
+ end
+
+ assert(info.flaglist, 'That is the only one I know how to handle')
+
+ 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
+
+ return result
+ else
+ local result = {}
+ for i = 1, #value do
+ result[value:sub(i, i)] = true
+ end
+
+ return result
+ end
+ end,
+
+ map = function(info, raw_value)
+ if type(raw_value) == 'table' then
+ return raw_value
+ end
+
+ assert(info.commalist, 'Only commas are supported currently')
+
+ local result = {}
+
+ 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)
+
+ result[key] = value
+ end
+
+ return result
+ end,
+}
+
+--- Converts a vimoption_T style value to a Lua value
+--- @private
+local function convert_value_to_lua(info, option_value)
+ return to_lua_value[info.metatype](info, option_value)
+end
+
+local prepend_methods = {
+ number = function()
+ error("The '^' operator is not currently supported for")
+ end,
+
+ string = function(left, right)
+ return right .. left
+ end,
+
+ array = function(left, right)
+ for i = #right, 1, -1 do
+ table.insert(left, 1, right[i])
+ end
+
+ return left
+ end,
+
+ map = tbl_merge,
+ set = tbl_merge,
+}
+
+--- Handles the '^' operator
+--- @private
+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
+
+local add_methods = {
+ number = function(left, right)
+ return left + right
+ end,
+
+ string = function(left, right)
+ return left .. right
+ end,
+
+ array = function(left, right)
+ for _, v in ipairs(right) do
+ table.insert(left, v)
+ end
+
+ return left
+ end,
+
+ map = tbl_merge,
+ set = tbl_merge,
+}
+
+--- Handles the '+' operator
+--- @private
+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
+
+--- @private
+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
+ end
+end
+
+local remove_methods = {
+ number = function(left, right)
+ return left - right
+ end,
+
+ string = function()
+ error('Subtraction not supported for strings.')
+ 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,
+
+ map = tbl_remove,
+ set = tbl_remove,
+}
+
+--- Handles the '-' operator
+--- @private
+local function remove_value(info, current, new)
+ return remove_methods[info.metatype](convert_value_to_lua(info, current), new)
+end
+
+--- @private
+local function create_option_accessor(scope)
+ local option_mt
+
+ --- @private
+ 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
+ assert(name == value._name, "must be the same value, otherwise that's weird.")
+
+ value = value._value
+ end
+
+ return setmetatable({
+ _name = name,
+ _value = value,
+ _info = info,
+ }, option_mt)
+ 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)
+ api.nvim_set_option_value(self._name, value, { scope = scope })
+ end,
+
+ get = function(self)
+ return convert_value_to_lua(self._info, self._value)
+ end,
+
+ append = function(self, right)
+ self._value = add_value(self._info, self._value, right)
+ self:_set()
+ end,
+
+ __add = function(self, right)
+ return make_option(self._name, add_value(self._info, self._value, right))
+ end,
+
+ prepend = function(self, right)
+ self._value = prepend_value(self._info, self._value, right)
+ self:_set()
+ end,
+
+ __pow = function(self, right)
+ return make_option(self._name, prepend_value(self._info, self._value, right))
+ end,
+
+ remove = function(self, right)
+ self._value = remove_value(self._info, self._value, right)
+ self:_set()
+ end,
+
+ __sub = function(self, right)
+ return make_option(self._name, remove_value(self._info, self._value, right))
+ end,
+ }
+ option_mt.__index = option_mt
+
+ return setmetatable({}, {
+ __index = function(_, k)
+ return make_option(k, api.nvim_get_option_value(k, {}))
+ end,
+
+ __newindex = function(_, k, v)
+ make_option(k, v):_set()
+ end,
+ })
+end
+
+---@addtogroup lua-vimscript
+---@brief <pre>help
+---` ` *vim.opt_local*
+--- *vim.opt_global*
+--- *vim.opt*
+---
+---
+---A special interface |vim.opt| exists for conveniently interacting with list-
+---and map-style option from Lua: It allows accessing them as Lua tables and
+---offers object-oriented method for adding and removing entries.
+---
+--- Examples: ~
+---
+--- The following methods of setting a list-style option are equivalent:
+--- In Vimscript: >vim
+--- set wildignore=*.o,*.a,__pycache__
+---<
+--- In Lua using `vim.o`: >lua
+--- vim.o.wildignore = '*.o,*.a,__pycache__'
+---<
+--- In Lua using `vim.opt`: >lua
+--- vim.opt.wildignore = { '*.o', '*.a', '__pycache__' }
+---<
+--- To replicate the behavior of |:set+=|, use: >lua
+---
+--- vim.opt.wildignore:append { "*.pyc", "node_modules" }
+---<
+--- To replicate the behavior of |:set^=|, use: >lua
+---
+--- vim.opt.wildignore:prepend { "new_first_value" }
+---<
+--- To replicate the behavior of |:set-=|, use: >lua
+---
+--- vim.opt.wildignore:remove { "node_modules" }
+---<
+--- The following methods of setting a map-style option are equivalent:
+--- In Vimscript: >vim
+--- set listchars=space:_,tab:>~
+---<
+--- In Lua using `vim.o`: >lua
+--- vim.o.listchars = 'space:_,tab:>~'
+---<
+--- In Lua using `vim.opt`: >lua
+--- vim.opt.listchars = { space = '_', tab = '>~' }
+---<
+---
+---Note that |vim.opt| returns an `Option` object, not the value of the option,
+---which is accessed through |vim.opt:get()|:
+---
+--- Examples: ~
+---
+--- The following methods of getting a list-style option are equivalent:
+--- In Vimscript: >vim
+--- echo wildignore
+---<
+--- In Lua using `vim.o`: >lua
+--- print(vim.o.wildignore)
+---<
+--- In Lua using `vim.opt`: >lua
+--- vim.print(vim.opt.wildignore:get())
+---<
+---
+---In any of the above examples, to replicate the behavior |:setlocal|, use
+---`vim.opt_local`. Additionally, to replicate the behavior of |:setglobal|, use
+---`vim.opt_global`.
+---</pre>
+
+--- @diagnostic disable-next-line:unused-local used for gen_vimdoc
+local Option = {} -- luacheck: no unused
+
+---Returns a Lua-representation of the option. Boolean, number and string
+---values will be returned in exactly the same fashion.
+---
+---For values that are comma-separated lists, an array will be returned with
+---the values as entries in the array: <pre>lua
+--- vim.cmd [[set wildignore=*.pyc,*.o]]
+---
+--- vim.print(vim.opt.wildignore:get())
+--- -- { "*.pyc", "*.o", }
+---
+--- for _, ignore_pattern in ipairs(vim.opt.wildignore:get()) do
+--- print("Will ignore:", ignore_pattern)
+--- end
+--- -- Will ignore: *.pyc
+--- -- Will ignore: *.o
+---</pre>
+---
+---For values that are comma-separated maps, a table will be returned with
+---the names as keys and the values as entries: <pre>lua
+--- vim.cmd [[set listchars=space:_,tab:>~]]
+---
+--- vim.print(vim.opt.listchars:get())
+--- -- { space = "_", tab = ">~", }
+---
+--- for char, representation in pairs(vim.opt.listchars:get()) do
+--- print(char, "=>", representation)
+--- end
+---</pre>
+---
+---For values that are lists of flags, a set will be returned with the flags
+---as keys and `true` as entries. <pre>lua
+--- vim.cmd [[set formatoptions=njtcroql]]
+---
+--- vim.print(vim.opt.formatoptions:get())
+--- -- { n = true, j = true, c = true, ... }
+---
+--- local format_opts = vim.opt.formatoptions:get()
+--- if format_opts.j then
+--- print("J is enabled!")
+--- end
+---</pre>
+---@return string|integer|boolean|nil value of option
+---@diagnostic disable-next-line:unused-local used for gen_vimdoc
+function Option:get() end
+
+---Append a value to string-style options. See |:set+=|
+---
+---These are equivalent: <pre>lua
+--- vim.opt.formatoptions:append('j')
+--- vim.opt.formatoptions = vim.opt.formatoptions + 'j'
+---</pre>
+---@param value string Value to append
+--- @diagnostic disable-next-line:unused-local used for gen_vimdoc
+function Option:append(value) end -- luacheck: no unused
+
+---Prepend a value to string-style options. See |:set^=|
+---
+---These are equivalent: <pre>lua
+--- vim.opt.wildignore:prepend('*.o')
+--- vim.opt.wildignore = vim.opt.wildignore ^ '*.o'
+---</pre>
+---@param value string Value to prepend
+---@diagnostic disable-next-line:unused-local used for gen_vimdoc
+function Option:prepend(value) end -- luacheck: no unused
+
+---Remove a value from string-style options. See |:set-=|
+---
+---These are equivalent: <pre>lua
+--- vim.opt.wildignore:remove('*.pyc')
+--- vim.opt.wildignore = vim.opt.wildignore - '*.pyc'
+---</pre>
+---@param value string Value to remove
+---@diagnostic disable-next-line:unused-local used for gen_vimdoc
+function Option:remove(value) end -- luacheck: no unused
+
+vim.opt = create_option_accessor()
+vim.opt_local = create_option_accessor('local')
+vim.opt_global = create_option_accessor('global')
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua
index 86e1adb49e..0eb782339c 100644
--- a/runtime/lua/vim/highlight.lua
+++ b/runtime/lua/vim/highlight.lua
@@ -1,3 +1,36 @@
+---@defgroup vim.highlight
+---
+---@brief
+---Nvim includes a function for highlighting a selection on yank.
+---
+---To enable it, add the following to your `init.vim`:
+---<pre>vim
+--- au TextYankPost * silent! lua vim.highlight.on_yank()
+---</pre>
+---
+---You can customize the highlight group and the duration of
+---the highlight via:
+---<pre>vim
+--- au TextYankPost * silent! lua vim.highlight.on_yank {higroup="IncSearch", timeout=150}
+---</pre>
+---
+---If you want to exclude visual selections from highlighting on yank, use:
+---<pre>vim
+--- au TextYankPost * silent! lua vim.highlight.on_yank {on_visual=false}
+---</pre>
+---
+--- <pre>help
+---vim.highlight.priorities *vim.highlight.priorities*
+---
+--- Table with default priorities used for highlighting:
+--- • `syntax`: `50`, used for standard syntax highlighting
+--- • `treesitter`: `100`, used for tree-sitter-based highlighting
+--- • `semantic_tokens`: `125`, used for LSP semantic token highlighting
+--- • `diagnostics`: `150`, used for code analysis such as diagnostics
+--- • `user`: `200`, used for user-triggered highlights such as LSP document
+--- symbols or `on_yank` autocommands
+---</pre>
+
local api = vim.api
local M = {}
@@ -10,7 +43,7 @@ M.priorities = {
user = 200,
}
---- Highlight range between two positions
+--- Apply highlight group to range of text.
---
---@param bufnr integer Buffer number to apply highlighting to
---@param ns integer Namespace to add highlight to
@@ -18,9 +51,9 @@ M.priorities = {
---@param start integer[]|string Start of region as a (line, column) tuple or string accepted by |getpos()|
---@param finish integer[]|string End of region as a (line, column) tuple or string accepted by |getpos()|
---@param opts table|nil Optional parameters
--- - regtype type of range (see |setreg()|, default charwise)
--- - inclusive boolean indicating whether the range is end-inclusive (default false)
--- - priority number indicating priority of highlight (default priorities.user)
+--- - regtype type of range (see |setreg()|, default charwise)
+--- - inclusive boolean indicating whether the range is end-inclusive (default false)
+--- - priority number indicating priority of highlight (default priorities.user)
function M.range(bufnr, ns, higroup, start, finish, opts)
opts = opts or {}
local regtype = opts.regtype or 'v'
@@ -46,22 +79,16 @@ end
local yank_ns = api.nvim_create_namespace('hlyank')
local yank_timer
---- Highlight the yanked region
----
---- use from init.vim via
---- au TextYankPost * lua vim.highlight.on_yank()
---- customize highlight group and timeout via
---- au TextYankPost * lua vim.highlight.on_yank {higroup="IncSearch", timeout=150}
---- customize conditions (here: do not highlight a visual selection) via
---- au TextYankPost * lua vim.highlight.on_yank {on_visual=false}
+
+--- Highlight the yanked text
---
--- @param opts table|nil Optional parameters
--- - higroup highlight group for yanked region (default "IncSearch")
--- - timeout time in ms before highlight is cleared (default 150)
--- - on_macro highlight when executing macro (default false)
--- - on_visual highlight when yanking visual selection (default true)
--- - event event structure (default vim.v.event)
--- - priority integer priority (default |vim.highlight.priorities|`.user`)
+--- @param opts table|nil Optional parameters
+--- - higroup highlight group for yanked region (default "IncSearch")
+--- - timeout time in ms before highlight is cleared (default 150)
+--- - on_macro highlight when executing macro (default false)
+--- - on_visual highlight when yanking visual selection (default true)
+--- - event event structure (default vim.v.event)
+--- - priority integer priority (default |vim.highlight.priorities|`.user`)
function M.on_yank(opts)
vim.validate({
opts = {
diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua
index 245a33625e..6c1afcad91 100644
--- a/runtime/lua/vim/iter.lua
+++ b/runtime/lua/vim/iter.lua
@@ -1,6 +1,6 @@
----@defgroup lua-iter
+---@defgroup vim.iter
---
---- @brief The \*vim.iter\* module provides a generic interface for working with
+--- This module provides a generic interface for working with
--- iterables: tables, lists, iterator functions, pair()/ipair()-like iterators,
--- and \`vim.iter()\` objects.
---
diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua
index cd28a9b54b..8ff8a19cb9 100644
--- a/runtime/lua/vim/version.lua
+++ b/runtime/lua/vim/version.lua
@@ -1,4 +1,4 @@
---- @defgroup lua-version
+--- @defgroup vim.version
---
--- @brief The \`vim.version\` module provides functions for comparing versions and ranges
--- conforming to the https://semver.org spec. Plugins, and plugin managers, can use this to check