diff options
-rw-r--r-- | cmake.deps/CMakeLists.txt | 22 | ||||
-rw-r--r-- | cmake.deps/cmake/GetBinaryDeps.cmake | 32 | ||||
-rw-r--r-- | runtime/doc/api.txt | 17 | ||||
-rw-r--r-- | runtime/doc/deprecated.txt | 1 | ||||
-rw-r--r-- | runtime/doc/treesitter.txt | 31 | ||||
-rw-r--r-- | runtime/lua/vim/filetype.lua | 1 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter.lua | 33 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/language.lua | 57 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/languagetree.lua | 7 | ||||
-rwxr-xr-x | src/nvim/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/nvim/api/deprecated.c | 14 | ||||
-rw-r--r-- | src/nvim/api/options.c | 47 | ||||
-rw-r--r-- | src/nvim/autocmd.c | 29 | ||||
-rw-r--r-- | src/nvim/edit.c | 6 | ||||
-rw-r--r-- | src/nvim/option.c | 31 | ||||
-rw-r--r-- | src/nvim/optionstr.c | 35 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 93 | ||||
-rw-r--r-- | test/old/testdir/test_filetype.vim | 1 |
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'], |