aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake.deps/CMakeLists.txt22
-rw-r--r--cmake.deps/cmake/GetBinaryDeps.cmake32
-rw-r--r--runtime/doc/api.txt17
-rw-r--r--runtime/doc/deprecated.txt1
-rw-r--r--runtime/doc/treesitter.txt31
-rw-r--r--runtime/lua/vim/filetype.lua1
-rw-r--r--runtime/lua/vim/treesitter.lua33
-rw-r--r--runtime/lua/vim/treesitter/language.lua57
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua7
-rwxr-xr-xsrc/nvim/CMakeLists.txt5
-rw-r--r--src/nvim/api/deprecated.c14
-rw-r--r--src/nvim/api/options.c47
-rw-r--r--src/nvim/autocmd.c29
-rw-r--r--src/nvim/edit.c6
-rw-r--r--src/nvim/option.c31
-rw-r--r--src/nvim/optionstr.c35
-rw-r--r--test/functional/api/vim_spec.lua93
-rw-r--r--test/old/testdir/test_filetype.vim1
18 files changed, 330 insertions, 132 deletions
diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt
index 665503aded..76eadb88bd 100644
--- a/cmake.deps/CMakeLists.txt
+++ b/cmake.deps/CMakeLists.txt
@@ -151,10 +151,10 @@ set(LUAROCKS_SHA256 a0b36cd68586cd79966d0106bb2e5a4f5523327867995fd66bee4237062b
set(UNIBILIUM_URL https://github.com/neovim/unibilium/archive/d72c3598e7ac5d1ebf86ee268b8b4ed95c0fa628.tar.gz)
set(UNIBILIUM_SHA256 9c4747c862ab5e3076dcf8fa8f0ea7a6b50f20ec5905618b9536655596797487)
-set(LIBTERMKEY_URL https://www.leonerd.org.uk/code/libtermkey/libtermkey-0.22.tar.gz)
+set(LIBTERMKEY_URL https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/libtermkey/0.22-1/libtermkey_0.22.orig.tar.gz)
set(LIBTERMKEY_SHA256 6945bd3c4aaa83da83d80a045c5563da4edd7d0374c62c0d35aec09eb3014600)
-set(LIBVTERM_URL https://www.leonerd.org.uk/code/libvterm/libvterm-0.3.1.tar.gz)
+set(LIBVTERM_URL https://launchpad.net/libvterm/trunk/v0.3/+download/libvterm-0.3.1.tar.gz)
set(LIBVTERM_SHA256 25a8ad9c15485368dfd0a8a9dca1aec8fea5c27da3fa74ec518d5d3787f0c397)
set(LUV_URL https://github.com/luvit/luv/archive/e5da6417db06a09d75bb5315662a2cf3e48a4a89.tar.gz)
@@ -163,9 +163,15 @@ set(LUV_SHA256 16720d17b9e6d42a7008b902207ea2d61e3eeaef3cf358398dbbf3777867ec09)
set(LUA_COMPAT53_URL https://github.com/keplerproject/lua-compat-5.3/archive/v0.9.tar.gz)
set(LUA_COMPAT53_SHA256 ad05540d2d96a48725bb79a1def35cf6652a4e2ec26376e2617c8ce2baa6f416)
-# cat.exe curl.exe curl-ca-bundle.crt diff.exe tee.exe xxd.exe
-set(WINTOOLS_URL https://github.com/neovim/deps/raw/c1e7dd8de9e1b18d11dcfa0a192cd029262e5303/opt/win32tools.zip)
-set(WINTOOLS_SHA256 3c4c490a3d392ceeb1347cb77cc821a31900b688a2189276d3a1131a3f21daf1)
+# Windows only: cat.exe diff.exe tee.exe xxd.exe
+set(CAT_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/cat.exe)
+set(CAT_SHA256 93b8d307bb15af3968920bdea3beb869a49d166f9164853c58a4e6ffdcae61c6)
+set(DIFF_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/diff.exe)
+set(DIFF_SHA256 4ceceebc8150422c6d8d9a06c2e9686d5a5d90f1033f60ad92ab81fe810e2a28)
+set(TEE_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/tee.exe)
+set(TEE_SHA256 950eea4e17fa3a7e89fa2c55374037b5797b3f1a54fea1304634884ab42ec14d)
+set(XXD_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/xxd.exe)
+set(XXD_SHA256 7a581e3882d28161cc52850f9a11d634b3eaf2c029276f093c1ed4c90e45a10c)
set(WINGUI_URL https://github.com/equalsraf/neovim-qt/releases/download/v0.2.17/neovim-qt.zip)
set(WINGUI_SHA256 502e386eef677c2c2e0c11d8cbb27f3e12b4d96818369417e8da4129c4580c25)
@@ -264,8 +270,10 @@ endif()
if(WIN32)
include(GetBinaryDeps)
- GetBinaryDep(TARGET wintools
- INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory . ${DEPS_BIN_DIR})
+ GetExecutable(TARGET cat)
+ GetExecutable(TARGET diff)
+ GetExecutable(TARGET tee)
+ GetExecutable(TARGET xxd)
if(USE_BUNDLED_NVIMQT)
GetBinaryDep(TARGET wingui
diff --git a/cmake.deps/cmake/GetBinaryDeps.cmake b/cmake.deps/cmake/GetBinaryDeps.cmake
index bac7dff919..48e66363d3 100644
--- a/cmake.deps/cmake/GetBinaryDeps.cmake
+++ b/cmake.deps/cmake/GetBinaryDeps.cmake
@@ -10,17 +10,10 @@ function(GetBinaryDep)
"INSTALL_COMMAND"
${ARGN})
- if(NOT _gettool_TARGET OR NOT _gettool_INSTALL_COMMAND)
- message(FATAL_ERROR "Must pass INSTALL_COMMAND and TARGET")
- endif()
-
string(TOUPPER "${_gettool_TARGET}_URL" URL_VARNAME)
string(TOUPPER "${_gettool_TARGET}_SHA256" HASH_VARNAME)
set(URL ${${URL_VARNAME}})
set(HASH ${${HASH_VARNAME}})
- if(NOT URL OR NOT HASH )
- message(FATAL_ERROR "${URL_VARNAME} and ${HASH_VARNAME} must be set")
- endif()
ExternalProject_Add(${_gettool_TARGET}
URL ${URL}
@@ -33,3 +26,28 @@ function(GetBinaryDep)
INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_BIN_DIR}
COMMAND "${_gettool_INSTALL_COMMAND}")
endfunction()
+
+# Download executable and move it to DEPS_BIN_DIR
+function(GetExecutable)
+ cmake_parse_arguments(ARG
+ ""
+ "TARGET"
+ ""
+ ${ARGN})
+
+ string(TOUPPER "${ARG_TARGET}_URL" URL_VARNAME)
+ string(TOUPPER "${ARG_TARGET}_SHA256" HASH_VARNAME)
+ set(URL ${${URL_VARNAME}})
+ set(HASH ${${HASH_VARNAME}})
+
+ ExternalProject_Add(${ARG_TARGET}
+ URL ${URL}
+ URL_HASH SHA256=${HASH}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}
+ DOWNLOAD_NO_EXTRACT TRUE
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_BIN_DIR}
+ COMMAND ${CMAKE_COMMAND} -E copy <DOWNLOADED_FILE> ${DEPS_BIN_DIR})
+endfunction()
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index ea57db22e0..b51da8d56f 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -1912,7 +1912,7 @@ nvim_get_all_options_info() *nvim_get_all_options_info()*
Gets the option information for all options.
The dictionary has the full option names as keys and option metadata
- dictionaries as detailed at |nvim_get_option_info()|.
+ dictionaries as detailed at |nvim_get_option_info2()|.
Return: ~
dictionary of all options
@@ -1926,8 +1926,8 @@ nvim_get_option({name}) *nvim_get_option()*
Return: ~
Option value (global)
-nvim_get_option_info({name}) *nvim_get_option_info()*
- Gets the option information for one option
+nvim_get_option_info2({name}, {*opts}) *nvim_get_option_info2()*
+ Gets the option information for one option from arbitrary buffer or window
Resulting dictionary has keys:
• name: Name of the option (like 'filetype')
@@ -1943,8 +1943,19 @@ nvim_get_option_info({name}) *nvim_get_option_info()*
• commalist: List of comma separated values
• flaglist: List of single char flags
+ When {scope} is not provided, the last set information applies to the
+ local value in the current buffer or window if it is available, otherwise
+ the global value information is returned. This behavior can be disabled by
+ explicitly specifying {scope} in the {opts} table.
+
Parameters: ~
• {name} Option name
+ • {opts} Optional parameters
+ • scope: One of "global" or "local". Analogous to |:setglobal|
+ and |:setlocal|, respectively.
+ • win: |window-ID|. Used for getting window local options.
+ • buf: Buffer number. Used for getting buffer local options.
+ Implies {scope} is "local".
Return: ~
Option Information
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index 69cec8da53..3eb2017bed 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -20,6 +20,7 @@ API
- *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead.
- *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead.
- *nvim_exec()* Use |nvim_exec2()| instead.
+- *nvim_get_option_info()* Use |nvim_get_option_info2()| instead.
COMMANDS
- *:rv* *:rviminfo* Deprecated alias to |:rshada| command.
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index d7e005ae51..96021d6272 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -592,8 +592,7 @@ get_node_text({node}, {source}, {opts})
(string)
get_parser({bufnr}, {lang}, {opts}) *vim.treesitter.get_parser()*
- Returns the parser for a specific buffer and filetype and attaches it to
- the buffer
+ Returns the parser for a specific buffer and attaches it to the buffer
If needed, this will create the parser.
@@ -728,29 +727,35 @@ stop({bufnr}) *vim.treesitter.stop()*
Lua module: vim.treesitter.language *lua-treesitter-language*
add({lang}, {opts}) *vim.treesitter.language.add()*
- Asserts that a parser for the language {lang} is installed.
+ Load parser with name {lang}
Parsers are searched in the `parser` runtime directory, or the provided
{path}
Parameters: ~
- • {lang} (string) Language the parser should parse (alphanumerical and
- `_` only)
+ • {lang} (string) Name of the parser (alphanumerical and `_` only)
• {opts} (table|nil) Options:
- • filetype (string|string[]) Filetype(s) that lang can be
- parsed with. Note this is not strictly the same as lang
- since a single lang can parse multiple filetypes. Defaults
- to lang.
+ • filetype (string|string[]) Default filetype the parser
+ should be associated with. Defaults to {lang}.
• path (string|nil) Optional path the parser is located at
• symbol_name (string|nil) Internal symbol name for the
language to load
+get_filetypes({lang}) *vim.treesitter.language.get_filetypes()*
+ Get the filetypes associated with the parser named {lang}.
+
+ Parameters: ~
+ • {lang} string Name of parser
+
+ Return: ~
+ string[] filetypes
+
get_lang({filetype}) *vim.treesitter.language.get_lang()*
Parameters: ~
- • {filetype} (string)
+ • {filetype} string
Return: ~
- (string|nil)
+ string|nil
inspect({lang}) *vim.treesitter.language.inspect()*
Inspects the provided language.
@@ -765,10 +770,10 @@ inspect({lang}) *vim.treesitter.language.inspect()*
(table)
register({lang}, {filetype}) *vim.treesitter.language.register()*
- Register a lang to be used for a filetype (or list of filetypes).
+ Register a parser named {lang} to be used for {filetype}(s).
Parameters: ~
- • {lang} (string) Language to register
+ • {lang} string Name of parser
• {filetype} string|string[] Filetype(s) to associate with lang
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 740304df15..87439f9f0c 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -615,6 +615,7 @@ local extension = {
asd = 'lisp',
lt = 'lite',
lite = 'lite',
+ livemd = 'livebook',
lgt = 'logtalk',
lotos = 'lotos',
lot = 'lotos',
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 4c3b17daa4..2594c1672d 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -58,9 +58,6 @@ function M._create_parser(bufnr, lang, opts)
vim.fn.bufload(bufnr)
- local ft = vim.bo[bufnr].filetype
- M.language.add(lang, { filetype = ft ~= '' and ft or nil })
-
local self = LanguageTree.new(bufnr, lang, opts)
---@private
@@ -94,7 +91,12 @@ function M._create_parser(bufnr, lang, opts)
return self
end
---- Returns the parser for a specific buffer and filetype and attaches it to the buffer
+--- @private
+local function valid_lang(lang)
+ return lang and lang ~= ''
+end
+
+--- Returns the parser for a specific buffer and attaches it to the buffer
---
--- If needed, this will create the parser.
---
@@ -110,18 +112,12 @@ function M.get_parser(bufnr, lang, opts)
bufnr = a.nvim_get_current_buf()
end
- if lang == nil then
- local ft = vim.bo[bufnr].filetype
- if ft ~= '' then
- lang = M.language.get_lang(ft) or ft
- -- TODO(lewis6991): we should error here and not default to ft
- -- if not lang then
- -- error(string.format('filetype %s of buffer %d is not associated with any lang', ft, bufnr))
- -- end
- else
- if parsers[bufnr] then
- return parsers[bufnr]
- end
+ if not valid_lang(lang) then
+ lang = M.language.get_lang(vim.bo[bufnr].filetype) or vim.bo[bufnr].filetype
+ end
+
+ if not valid_lang(lang) then
+ if not parsers[bufnr] then
error(
string.format(
'There is no parser available for buffer %d and one could not be'
@@ -131,9 +127,7 @@ function M.get_parser(bufnr, lang, opts)
)
)
end
- end
-
- if parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
+ elseif parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
parsers[bufnr] = M._create_parser(bufnr, lang, opts)
end
@@ -164,7 +158,6 @@ function M.get_string_parser(str, lang, opts)
str = { str, 'string' },
lang = { lang, 'string' },
})
- M.language.add(lang)
return LanguageTree.new(str, lang, opts)
end
diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua
index 974d66ec05..b1c788e6ba 100644
--- a/runtime/lua/vim/treesitter/language.lua
+++ b/runtime/lua/vim/treesitter/language.lua
@@ -6,9 +6,25 @@ local M = {}
---@type table<string,string>
local ft_to_lang = {}
----@param filetype string
----@return string|nil
+--- Get the filetypes associated with the parser named {lang}.
+--- @param lang string Name of parser
+--- @return string[] filetypes
+function M.get_filetypes(lang)
+ local r = {} ---@type string[]
+ for ft, p in pairs(ft_to_lang) do
+ if p == lang then
+ r[#r + 1] = ft
+ end
+ end
+ return r
+end
+
+--- @param filetype string
+--- @return string|nil
function M.get_lang(filetype)
+ if filetype == '' then
+ return
+ end
return ft_to_lang[filetype]
end
@@ -35,16 +51,14 @@ end
---@field filetype? string|string[]
---@field symbol_name? string
---- Asserts that a parser for the language {lang} is installed.
+--- Load parser with name {lang}
---
--- Parsers are searched in the `parser` runtime directory, or the provided {path}
---
----@param lang string Language the parser should parse (alphanumerical and `_` only)
+---@param lang string Name of the parser (alphanumerical and `_` only)
---@param opts (table|nil) Options:
---- - filetype (string|string[]) Filetype(s) that lang can be parsed with.
---- Note this is not strictly the same as lang since a single lang can
---- parse multiple filetypes.
---- Defaults to lang.
+--- - filetype (string|string[]) Default filetype the parser should be associated with.
+--- Defaults to {lang}.
--- - path (string|nil) Optional path the parser is located at
--- - symbol_name (string|nil) Internal symbol name for the language to load
function M.add(lang, opts)
@@ -61,7 +75,7 @@ function M.add(lang, opts)
filetype = { filetype, { 'string', 'table' }, true },
})
- M.register(lang, filetype or lang)
+ M.register(lang, filetype)
if vim._ts_has_language(lang) then
return
@@ -83,23 +97,26 @@ function M.add(lang, opts)
vim._ts_add_language(path, lang, symbol_name)
end
---- Register a lang to be used for a filetype (or list of filetypes).
----@param lang string Language to register
----@param filetype string|string[] Filetype(s) to associate with lang
+--- @private
+--- @param x string|string[]
+--- @return string[]
+local function ensure_list(x)
+ if type(x) == 'table' then
+ return x
+ end
+ return { x }
+end
+
+--- Register a parser named {lang} to be used for {filetype}(s).
+--- @param lang string Name of parser
+--- @param filetype string|string[] Filetype(s) to associate with lang
function M.register(lang, filetype)
vim.validate({
lang = { lang, 'string' },
filetype = { filetype, { 'string', 'table' } },
})
- local filetypes ---@type string[]
- if type(filetype) == 'string' then
- filetypes = { filetype }
- else
- filetypes = filetype
- end
-
- for _, f in ipairs(filetypes) do
+ for _, f in ipairs(ensure_list(filetype)) do
if f ~= '' then
ft_to_lang[f] = lang
end
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index 82e507551d..922e4881ca 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -76,7 +76,7 @@ LanguageTree.__index = LanguageTree
--- "injected" language parsers, which themselves may inject other languages, recursively.
---
---@param source (integer|string) Buffer or text string to parse
----@param lang string Root language of this tree
+---@param lang string|nil Root language of this tree
---@param opts (table|nil) Optional arguments:
--- - injections table Map of language to injection query strings. Overrides the
--- built-in runtime file searching for language injections.
@@ -86,11 +86,6 @@ function LanguageTree.new(source, lang, opts)
---@type LanguageTreeOpts
opts = opts or {}
- if opts.queries then
- a.nvim_err_writeln("'queries' is no longer supported. Use 'injections' now")
- opts.injections = opts.queries
- end
-
local injections = opts.injections or {}
local self = setmetatable({
_source = source,
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index d425e819f0..cb688785df 100755
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -746,15 +746,12 @@ if(WIN32)
"file(MAKE_DIRECTORY \"${PROJECT_BINARY_DIR}/windows_runtime_deps/platforms\")")
foreach(DEP_FILE IN ITEMS
cat.exe
- curl-ca-bundle.crt
- curl.exe
diff.exe
tee.exe
win32yank.exe
xxd.exe
- ${NVIMQT_DEPS}
- )
+ ${NVIMQT_DEPS})
get_filename_component(DEP_FILE_DIR ${DEP_FILE} DIRECTORY)
set(EXTERNAL_BLOBS_SCRIPT "${EXTERNAL_BLOBS_SCRIPT}\n"
"file(COPY \"${DEPS_PREFIX}/bin/${DEP_FILE}\"
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index 6a12cfe2da..5937b2f635 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -20,6 +20,7 @@
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
+#include "nvim/option.h"
#include "nvim/pos.h"
#include "nvim/types.h"
@@ -508,3 +509,16 @@ static int64_t convert_index(int64_t index)
{
return index < 0 ? index - 1 : index;
}
+
+/// Gets the option information for one option
+///
+/// @deprecated Use @ref nvim_get_option_info2 instead.
+///
+/// @param name Option name
+/// @param[out] err Error details, if any
+/// @return Option Information
+Dictionary nvim_get_option_info(String name, Error *err)
+ FUNC_API_SINCE(7)
+{
+ return get_vimoption(name, OPT_GLOBAL, curbuf, curwin, err);
+}
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 7a453c01b4..baeb3e88fb 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -111,15 +111,15 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err)
// Set curwin/curbuf to buf and save a few things.
aucmd_prepbuf(aco, ftbuf);
- set_option_value("bufhidden", 0L, "hide", OPT_LOCAL);
- set_option_value("buftype", 0L, "nofile", OPT_LOCAL);
- set_option_value("swapfile", 0L, NULL, OPT_LOCAL);
- set_option_value("modeline", 0L, NULL, OPT_LOCAL); // 'nomodeline'
-
- ftbuf->b_p_ft = xstrdup(filetype);
TRY_WRAP(err, {
- apply_autocmds(EVENT_FILETYPE, ftbuf->b_p_ft, ftbuf->b_fname, true, ftbuf);
+ set_option_value("bufhidden", 0L, "hide", OPT_LOCAL);
+ set_option_value("buftype", 0L, "nofile", OPT_LOCAL);
+ set_option_value("swapfile", 0L, NULL, OPT_LOCAL);
+ set_option_value("modeline", 0L, NULL, OPT_LOCAL); // 'nomodeline'
+
+ ftbuf->b_p_ft = xstrdup(filetype);
+ do_filetype_autocmd(ftbuf, false);
});
return ftbuf;
@@ -283,7 +283,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
/// Gets the option information for all options.
///
/// The dictionary has the full option names as keys and option metadata
-/// dictionaries as detailed at |nvim_get_option_info()|.
+/// dictionaries as detailed at |nvim_get_option_info2()|.
///
/// @return dictionary of all options
Dictionary nvim_get_all_options_info(Error *err)
@@ -292,7 +292,7 @@ Dictionary nvim_get_all_options_info(Error *err)
return get_all_vimoptions();
}
-/// Gets the option information for one option
+/// Gets the option information for one option from arbitrary buffer or window
///
/// Resulting dictionary has keys:
/// - name: Name of the option (like 'filetype')
@@ -311,15 +311,36 @@ Dictionary nvim_get_all_options_info(Error *err)
/// - commalist: List of comma separated values
/// - flaglist: List of single char flags
///
+/// When {scope} is not provided, the last set information applies to the local
+/// value in the current buffer or window if it is available, otherwise the
+/// global value information is returned. This behavior can be disabled by
+/// explicitly specifying {scope} in the {opts} table.
///
-/// @param name Option name
+/// @param name Option name
+/// @param opts Optional parameters
+/// - scope: One of "global" or "local". Analogous to
+/// |:setglobal| and |:setlocal|, respectively.
+/// - win: |window-ID|. Used for getting window local options.
+/// - buf: Buffer number. Used for getting buffer local options.
+/// Implies {scope} is "local".
/// @param[out] err Error details, if any
/// @return Option Information
-Dictionary nvim_get_option_info(String name, Error *err)
- FUNC_API_SINCE(7)
+Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err)
+ FUNC_API_SINCE(11)
{
- return get_vimoption(name, err);
+ int scope = 0;
+ int opt_type = SREQ_GLOBAL;
+ void *from = NULL;
+ if (!validate_option_value_args(opts, &scope, &opt_type, &from, NULL, err)) {
+ return (Dictionary)ARRAY_DICT_INIT;
+ }
+
+ buf_T *buf = (opt_type == SREQ_BUF) ? (buf_T *)from : curbuf;
+ win_T *win = (opt_type == SREQ_WIN) ? (win_T *)from : curwin;
+
+ return get_vimoption(name, scope, buf, win, err);
}
+
/// Sets the global value of an option.
///
/// @param channel_id
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index c66ee4286e..2e83260a40 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -2758,3 +2758,32 @@ void do_autocmd_focusgained(bool gained)
recursive = false;
}
+
+void do_filetype_autocmd(buf_T *buf, bool force)
+{
+ static int ft_recursive = 0;
+
+ if (ft_recursive > 0 && !force) {
+ return; // disallow recursion
+ }
+
+ char **varp = &buf->b_p_ft;
+ int secure_save = secure;
+
+ // Reset the secure flag, since the value of 'filetype' has
+ // been checked to be safe.
+ secure = 0;
+
+ ft_recursive++;
+ did_filetype = true;
+ // Only pass true for "force" when it is true or
+ // used recursively, to avoid endless recurrence.
+ apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, force, buf);
+ ft_recursive--;
+
+ // Just in case the old "buf" is now invalid
+ if (varp != &(buf->b_p_ft)) {
+ varp = NULL;
+ }
+ secure = secure_save;
+}
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 0983d025e5..26101d06ed 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -2294,11 +2294,11 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
// Don't do it when "restart_edit" was set and nothing was inserted,
// otherwise CTRL-O w and then <Left> will clear "last_insert".
ptr = get_inserted();
- if (did_restart_edit == 0 || (ptr != NULL
- && (int)strlen(ptr) > new_insert_skip)) {
+ int added = ptr == NULL ? 0 : (int)strlen(ptr) - new_insert_skip;
+ if (did_restart_edit == 0 || added > 0) {
xfree(last_insert);
last_insert = ptr;
- last_insert_skip = new_insert_skip;
+ last_insert_skip = added < 0 ? 0 : new_insert_skip;
} else {
xfree(ptr);
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 0aa2f8ab04..f672b3ab6f 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5605,26 +5605,27 @@ long get_sidescrolloff_value(win_T *wp)
return wp->w_p_siso < 0 ? p_siso : wp->w_p_siso;
}
-Dictionary get_vimoption(String name, Error *err)
+Dictionary get_vimoption(String name, int scope, buf_T *buf, win_T *win, Error *err)
{
int opt_idx = findoption_len((const char *)name.data, name.size);
VALIDATE_S(opt_idx >= 0, "option (not found)", name.data, {
return (Dictionary)ARRAY_DICT_INIT;
});
- return vimoption2dict(&options[opt_idx]);
+
+ return vimoption2dict(&options[opt_idx], scope, buf, win);
}
Dictionary get_all_vimoptions(void)
{
Dictionary retval = ARRAY_DICT_INIT;
for (size_t i = 0; options[i].fullname != NULL; i++) {
- Dictionary opt_dict = vimoption2dict(&options[i]);
+ Dictionary opt_dict = vimoption2dict(&options[i], OPT_GLOBAL, curbuf, curwin);
PUT(retval, options[i].fullname, DICTIONARY_OBJ(opt_dict));
}
return retval;
}
-static Dictionary vimoption2dict(vimoption_T *opt)
+static Dictionary vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *win)
{
Dictionary dict = ARRAY_DICT_INIT;
@@ -5649,9 +5650,25 @@ static Dictionary vimoption2dict(vimoption_T *opt)
PUT(dict, "was_set", BOOL(opt->flags & P_WAS_SET));
- PUT(dict, "last_set_sid", INTEGER_OBJ(opt->last_set.script_ctx.sc_sid));
- PUT(dict, "last_set_linenr", INTEGER_OBJ(opt->last_set.script_ctx.sc_lnum));
- PUT(dict, "last_set_chan", INTEGER_OBJ((int64_t)opt->last_set.channel_id));
+ LastSet last_set = { .channel_id = 0 };
+ if (req_scope == OPT_GLOBAL) {
+ last_set = opt->last_set;
+ } else {
+ // Scope is either OPT_LOCAL or a fallback mode was requested.
+ if (opt->indir & PV_BUF) {
+ last_set = buf->b_p_script_ctx[opt->indir & PV_MASK];
+ }
+ if (opt->indir & PV_WIN) {
+ last_set = win->w_p_script_ctx[opt->indir & PV_MASK];
+ }
+ if (req_scope != OPT_LOCAL && last_set.script_ctx.sc_sid == 0) {
+ last_set = opt->last_set;
+ }
+ }
+
+ PUT(dict, "last_set_sid", INTEGER_OBJ(last_set.script_ctx.sc_sid));
+ PUT(dict, "last_set_linenr", INTEGER_OBJ(last_set.script_ctx.sc_lnum));
+ PUT(dict, "last_set_chan", INTEGER_OBJ((int64_t)last_set.channel_id));
const char *type;
Object def;
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 170800b4e6..bf4ebbb3e2 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -1529,34 +1529,6 @@ static void do_syntax_autocmd(buf_T *buf, bool value_changed)
syn_recursive--;
}
-static void do_filetype_autocmd(buf_T *buf, char **varp, int opt_flags, bool value_changed)
-{
- // 'filetype' is set, trigger the FileType autocommand
- // Skip this when called from a modeline and the filetype was
- // already set to this value.
- if (!(opt_flags & OPT_MODELINE) || value_changed) {
- static int ft_recursive = 0;
- int secure_save = secure;
-
- // Reset the secure flag, since the value of 'filetype' has
- // been checked to be safe.
- secure = 0;
-
- ft_recursive++;
- did_filetype = true;
- // Only pass true for "force" when the value changed or not
- // used recursively, to avoid endless recurrence.
- apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname,
- value_changed || ft_recursive == 1, buf);
- ft_recursive--;
- // Just in case the old "buf" is now invalid
- if (varp != &(buf->b_p_ft)) {
- varp = NULL;
- }
- secure = secure_save;
- }
-}
-
static void do_spelllang_source(win_T *win)
{
char fname[200];
@@ -1884,7 +1856,12 @@ static char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx, char
if (varp == &buf->b_p_syn) {
do_syntax_autocmd(buf, value_changed);
} else if (varp == &buf->b_p_ft) {
- do_filetype_autocmd(buf, varp, opt_flags, value_changed);
+ // 'filetype' is set, trigger the FileType autocommand
+ // Skip this when called from a modeline
+ // Force autocmd when the filetype was changed
+ if (!(opt_flags & OPT_MODELINE) || value_changed) {
+ do_filetype_autocmd(buf, value_changed);
+ }
} else if (varp == &win->w_s->b_p_spl) {
do_spelllang_source(win);
}
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index d8fb1bc623..9d5a0c4b4e 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -2892,6 +2892,99 @@ describe('API', function()
end)
end)
+ describe('nvim_get_option_info2', function()
+ local fname
+ local bufs
+ local wins
+
+ before_each(function()
+ fname = tmpname()
+ write_file(fname, [[
+ setglobal dictionary=mydict " 1, global-local (buffer)
+ setlocal formatprg=myprg " 2, global-local (buffer)
+ setglobal equalprg=prg1 " 3, global-local (buffer)
+ setlocal equalprg=prg2 " 4, global-local (buffer)
+ setglobal fillchars=stl:x " 5, global-local (window)
+ setlocal listchars=eol:c " 6, global-local (window)
+ setglobal showbreak=aaa " 7, global-local (window)
+ setlocal showbreak=bbb " 8, global-local (window)
+ setglobal completeopt=menu " 9, global
+ ]])
+
+ exec_lua 'vim.cmd.vsplit()'
+ meths.create_buf(false, false)
+
+ bufs = meths.list_bufs()
+ wins = meths.list_wins()
+
+ meths.win_set_buf(wins[1].id, bufs[1].id)
+ meths.win_set_buf(wins[2].id, bufs[2].id)
+
+ meths.set_current_win(wins[2].id)
+ meths.exec('source ' .. fname, false)
+
+ meths.set_current_win(wins[1].id)
+ end)
+
+ after_each(function()
+ os.remove(fname)
+ end)
+
+ it('should return option information', function()
+ eq(meths.get_option_info('dictionary'), meths.get_option_info2('dictionary', {})) -- buffer
+ eq(meths.get_option_info('fillchars'), meths.get_option_info2('fillchars', {})) -- window
+ eq(meths.get_option_info('completeopt'), meths.get_option_info2('completeopt', {})) -- global
+ end)
+
+ describe('last set', function()
+ local tests = {
+ {desc="(buf option, global requested, global set) points to global", linenr=1, sid=1, args={'dictionary', {scope='global'}}},
+ {desc="(buf option, global requested, local set) is not set", linenr=0, sid=0, args={'formatprg', {scope='global'}}},
+ {desc="(buf option, global requested, both set) points to global", linenr=3, sid=1, args={'equalprg', {scope='global'}}},
+ {desc="(buf option, local requested, global set) is not set", linenr=0, sid=0, args={'dictionary', {scope='local'}}},
+ {desc="(buf option, local requested, local set) points to local", linenr=2, sid=1, args={'formatprg', {scope='local'}}},
+ {desc="(buf option, local requested, both set) points to local", linenr=4, sid=1, args={'equalprg', {scope='local'}}},
+ {desc="(buf option, fallback requested, global set) points to global", linenr=1, sid=1, args={'dictionary', {}}},
+ {desc="(buf option, fallback requested, local set) points to local", linenr=2, sid=1, args={'formatprg', {}}},
+ {desc="(buf option, fallback requested, both set) points to local", linenr=4, sid=1, args={'equalprg', {}}},
+ {desc="(win option, global requested, global set) points to global", linenr=5, sid=1, args={'fillchars', {scope='global'}}},
+ {desc="(win option, global requested, local set) is not set", linenr=0, sid=0, args={'listchars', {scope='global'}}},
+ {desc="(win option, global requested, both set) points to global", linenr=7, sid=1, args={'showbreak', {scope='global'}}},
+ {desc="(win option, local requested, global set) is not set", linenr=0, sid=0, args={'fillchars', {scope='local'}}},
+ {desc="(win option, local requested, local set) points to local", linenr=6, sid=1, args={'listchars', {scope='local'}}},
+ {desc="(win option, local requested, both set) points to local", linenr=8, sid=1, args={'showbreak', {scope='local'}}},
+ {desc="(win option, fallback requested, global set) points to global", linenr=5, sid=1, args={'fillchars', {}}},
+ {desc="(win option, fallback requested, local set) points to local", linenr=6, sid=1, args={'listchars', {}}},
+ {desc="(win option, fallback requested, both set) points to local", linenr=8, sid=1, args={'showbreak', {}}},
+ {desc="(global option, global requested) points to global", linenr=9, sid=1, args={'completeopt', {scope='global'}}},
+ {desc="(global option, local requested) is not set", linenr=0, sid=0, args={'completeopt', {scope='local'}}},
+ {desc="(global option, fallback requested) points to global", linenr=9, sid=1, args={'completeopt', {}}},
+ }
+
+ for _, t in pairs(tests) do
+ it(t.desc, function()
+ -- Switch to the target buffer/window so that curbuf/curwin are used.
+ meths.set_current_win(wins[2].id)
+ local info = meths.get_option_info2(unpack(t.args))
+ eq(t.linenr, info.last_set_linenr)
+ eq(t.sid, info.last_set_sid)
+ end)
+ end
+
+ it('is provided for cross-buffer requests', function()
+ local info = meths.get_option_info2('formatprg', {buf=bufs[2].id})
+ eq(2, info.last_set_linenr)
+ eq(1, info.last_set_sid)
+ end)
+
+ it('is provided for cross-window requests', function()
+ local info = meths.get_option_info2('listchars', {win=wins[2].id})
+ eq(6, info.last_set_linenr)
+ eq(1, info.last_set_sid)
+ end)
+ end)
+ end)
+
describe('nvim_echo', function()
local screen
diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim
index 29eb0c599d..23adff3d3d 100644
--- a/test/old/testdir/test_filetype.vim
+++ b/test/old/testdir/test_filetype.vim
@@ -338,6 +338,7 @@ let s:filename_checks = {
\ 'lite': ['file.lite', 'file.lt'],
\ 'litestep': ['/LiteStep/any/file.rc', 'any/LiteStep/any/file.rc'],
\ 'logcheck': ['/etc/logcheck/file.d-some/file', '/etc/logcheck/file.d/file', 'any/etc/logcheck/file.d-some/file', 'any/etc/logcheck/file.d/file'],
+ \ 'livebook': ['file.livemd'],
\ 'loginaccess': ['/etc/login.access', 'any/etc/login.access'],
\ 'logindefs': ['/etc/login.defs', 'any/etc/login.defs'],
\ 'logtalk': ['file.lgt'],