diff --git a/README.md b/README.md index 4ec5cf2be3..b5a469b32d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -[![Neovim](https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo-300x87.png)](https://neovim.io) +

+ Neovim +

[Documentation](https://neovim.io/doc/general/) | [Chat](https://app.element.io/#/room/#neovim:matrix.org) | diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 8323ec3e9d..c0a7b73438 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3670,8 +3670,7 @@ A jump table for the options with a short description can be found at |Q_op|. 0: never 1: only if there are at least two windows 2: always - 3: have a global statusline at the bottom instead of one for - each window + 3: always and ONLY the last window The screen looks nicer with a status line if you have several windows, but it takes another screen line. |status-line| diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 994f97bba0..508565dea4 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -182,8 +182,7 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. *:so* *:source* *load-vim-script* :[range]so[urce] [file] Runs |Ex| commands or Lua code (".lua" files) from - [file], or from the current buffer if no [file] is - given. + [file], or current buffer if no [file]. Triggers the |SourcePre| autocommand. *:source!* :[range]so[urce]! {file} diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 9ca5faf112..59f085977b 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -180,6 +180,7 @@ Commands: |:Man| is available by default, with many improvements such as completion |:sign-define| accepts a `numhl` argument, to highlight the line number |:match| can be invoked before highlight group is defined + |:source| works with Lua and anonymous (no file) scripts Events: |RecordingEnter| diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fbbc543893..6c72c2866e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9217,6 +9217,8 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va /// Finds the dict (g:, l:, s:, …) and hashtable used for a variable. /// +/// Assigns SID if s: scope is accessed from Lua or anonymous Vimscript. #15994 +/// /// @param[in] name Variable name, possibly with scope prefix. /// @param[in] name_len Variable name length. /// @param[out] varname Will be set to the start of the name without scope @@ -9304,6 +9306,7 @@ static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, cons } } if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) { + // Create SID if s: scope is accessed from Lua or anon Vimscript. #15994 new_script_item(NULL, ¤t_sctx.sc_sid); } *d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict; diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index b80004f67c..94176fc2ce 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -65,6 +65,7 @@ describe('nvim_get_commands', function() local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, keepscript=false, script_id=3, } local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, keepscript=false, script_id=4, } source([[ + let s:foo = 1 command -complete=custom,ListUsers -nargs=+ Finger !finger ]]) eq({Finger=cmd1}, meths.get_commands({builtin=false})) @@ -72,12 +73,18 @@ describe('nvim_get_commands', function() eq({Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false})) source([[ + function! s:foo() abort + endfunction command -bang -nargs=* Cmd2 call foo() ]]) source([[ + function! s:ohyeah() abort + endfunction command -bar -nargs=0 Cmd3 call ohyeah() ]]) source([[ + function! s:just_great() abort + endfunction command -register Cmd4 call just_great() ]]) -- TODO(justinmk): Order is stable but undefined. Sort before return? diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua index 0b2190bbcf..64cf53dfa9 100644 --- a/test/functional/ex_cmds/cmd_map_spec.lua +++ b/test/functional/ex_cmds/cmd_map_spec.lua @@ -1,21 +1,23 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear -local feed_command = helpers.feed_command local feed = helpers.feed local eq = helpers.eq local expect = helpers.expect local eval = helpers.eval local funcs = helpers.funcs local insert = helpers.insert +local write_file = helpers.write_file local exc_exec = helpers.exc_exec -local source = helpers.source +local command = helpers.command local Screen = require('test.functional.ui.screen') describe('mappings with ', function() local screen + local tmpfile = 'X_ex_cmds_cmd_map' + local function cmdmap(lhs, rhs) - feed_command('noremap '..lhs..' '..rhs..'') - feed_command('noremap! '..lhs..' '..rhs..'') + command('noremap '..lhs..' '..rhs..'') + command('noremap! '..lhs..' '..rhs..'') end before_each(function() @@ -39,7 +41,7 @@ describe('mappings with ', function() cmdmap('', 'normal! ww') cmdmap('', 'normal! "ay') cmdmap('', 'throw "very error"') - feed_command([[ + command([[ function! TextObj() if mode() !=# "v" normal! v @@ -55,11 +57,15 @@ describe('mappings with ', function() feed('gg') cmdmap('', 'startinsert') cmdmap('', 'stopinsert') - feed_command("abbr foo let g:y = 17bar") + command("abbr foo let g:y = 17bar") + end) + + after_each(function() + os.remove(tmpfile) end) it('can be displayed', function() - feed_command('map ') + command('map ') screen:expect([[ ^some short lines | of test text | @@ -73,8 +79,8 @@ describe('mappings with ', function() end) it('handles invalid mappings', function() - feed_command('let x = 0') - feed_command('noremap let x = 1') + command('let x = 0') + command('noremap let x = 1') feed('') screen:expect([[ ^some short lines | @@ -87,7 +93,7 @@ describe('mappings with ', function() {2:E5521: mapping must end with before second } | ]]) - feed_command('noremap let x = 2') + command('noremap let x = 2') feed('') screen:expect([[ ^some short lines | @@ -100,7 +106,7 @@ describe('mappings with ', function() {2:E5522: mapping must not include key} | ]]) - feed_command('noremap let x = 3') + command('noremap let x = 3') feed('') screen:expect([[ ^some short lines | @@ -137,7 +143,7 @@ describe('mappings with ', function() eq('n', eval('mode(1)')) -- select mode mapping - feed_command('snoremap let m = mode(1)') + command('snoremap let m = mode(1)') feed('gh') eq('s', eval('m')) -- didn't leave select mode @@ -184,8 +190,8 @@ describe('mappings with ', function() eq('n', eval('mode(1)')) -- terminal mode - feed_command('tnoremap let m = mode(1)') - feed_command('split | terminal') + command('tnoremap let m = mode(1)') + command('split | terminal') feed('i') eq('t', eval('mode(1)')) feed('') @@ -264,11 +270,11 @@ describe('mappings with ', function() end) it('works in :normal command', function() - feed_command('noremap ,x call append(1, "xx")\\| call append(1, "aa")') - feed_command('noremap ,f nosuchcommand') - feed_command('noremap ,e throw "very error"\\| call append(1, "yy")') - feed_command('noremap ,m echoerr "The message."\\| call append(1, "zz")') - feed_command('noremap ,w for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor') + command('noremap ,x call append(1, "xx")\\| call append(1, "aa")') + command('noremap ,f nosuchcommand') + command('noremap ,e throw "very error"\\| call append(1, "yy")') + command('noremap ,m echoerr "The message."\\| call append(1, "zz")') + command('noremap ,w for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor') feed(":normal ,x") screen:expect([[ @@ -297,7 +303,7 @@ describe('mappings with ', function() :normal ,x | ]]) - feed_command(':%d') + command(':%d') eq('Vim(echoerr):Err', exc_exec("normal ,w")) screen:expect([[ ^ | @@ -310,8 +316,8 @@ describe('mappings with ', function() --No lines in buffer-- | ]]) - feed_command(':%d') - feed_command(':normal ,w') + command(':%d') + feed(':normal ,w') screen:expect([[ ^ | 4 | @@ -401,8 +407,8 @@ describe('mappings with ', function() end) it('works in select mode', function() - feed_command('snoremap throw "very error"') - feed_command('snoremap normal! "by') + command('snoremap throw "very error"') + command('snoremap normal! "by') -- can extend select mode feed('gh') screen:expect([[ @@ -830,12 +836,14 @@ describe('mappings with ', function() end) it("works with mappings", function() - source([[ + command('new!') + write_file(tmpfile, [[ map call do_it() function! s:do_it() let g:x = 10 endfunction ]]) + command('source '..tmpfile) feed('') eq('', eval('v:errmsg')) eq(10, eval('g:x')) diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua index d320425de1..a6be04138b 100644 --- a/test/functional/ex_cmds/echo_spec.lua +++ b/test/functional/ex_cmds/echo_spec.lua @@ -181,9 +181,9 @@ describe(':echo :echon :echomsg :echoerr', function() end) it('dumps references to script functions', function() - eq('2_Test2', eval('String(Test2_f)')) - eq("function('2_Test2')", eval('StringMsg(Test2_f)')) - eq("function('2_Test2')", eval('StringErr(Test2_f)')) + eq('1_Test2', eval('String(Test2_f)')) + eq("function('1_Test2')", eval('StringMsg(Test2_f)')) + eq("function('1_Test2')", eval('StringErr(Test2_f)')) end) it('dump references to lambdas', function() @@ -205,11 +205,11 @@ describe(':echo :echon :echomsg :echoerr', function() it('dumps automatically created partials', function() assert_same_echo_dump( - "function('2_Test2', {'f': function('2_Test2')})", + "function('1_Test2', {'f': function('1_Test2')})", '{"f": Test2_f}.f', true) assert_same_echo_dump( - "function('2_Test2', [1], {'f': function('2_Test2', [1])})", + "function('1_Test2', [1], {'f': function('1_Test2', [1])})", '{"f": function(Test2_f, [1])}.f', true) end) @@ -227,7 +227,7 @@ describe(':echo :echon :echomsg :echoerr', function() function() meths.set_var('d', {v=true}) eq(dedent([[ - {'p': function('2_Test2', {...@0}), 'f': function('2_Test2'), 'v': v:true}]]), + {'p': function('1_Test2', {...@0}), 'f': function('1_Test2'), 'v': v:true}]]), exec_capture('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) end) @@ -264,7 +264,7 @@ describe(':echo :echon :echomsg :echoerr', function() eval('add(l, function("Test1", l))') eval('add(l, function("Test1", d))') eq(dedent([=[ - {'p': function('2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('2_Test2'), 'v': v:true}]=]), + {'p': function('1_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('1_Test2'), 'v': v:true}]=]), exec_capture('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) end) end) diff --git a/test/functional/ex_cmds/script_spec.lua b/test/functional/ex_cmds/script_spec.lua index f249b9ba7c..bf69ada820 100644 --- a/test/functional/ex_cmds/script_spec.lua +++ b/test/functional/ex_cmds/script_spec.lua @@ -2,18 +2,30 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq local neq = helpers.neq +local command = helpers.command +local write_file = helpers.write_file local meths = helpers.meths local clear = helpers.clear local dedent = helpers.dedent -local source = helpers.source local exc_exec = helpers.exc_exec local missing_provider = helpers.missing_provider +local tmpfile = 'X_ex_cmds_script' + before_each(clear) +local function source(code) + write_file(tmpfile, code) + command('source '..tmpfile) +end + describe('script_get-based command', function() local garbage = ')}{+*({}]*[;(+}{&[]}{*])(' + after_each(function() + os.remove(tmpfile) + end) + local function test_garbage_exec(cmd, check_neq) describe(cmd, function() it('works correctly when skipping oneline variant', function() diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 1845786c4b..173fc54af5 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -21,7 +21,6 @@ local map = global_helpers.tbl_map local ok = global_helpers.ok local sleep = global_helpers.sleep local tbl_contains = global_helpers.tbl_contains -local write_file = global_helpers.write_file local fail = global_helpers.fail local module = { @@ -54,7 +53,6 @@ if module.nvim_dir == module.nvim_prog then module.nvim_dir = "." end -local tmpname = global_helpers.tmpname local iswin = global_helpers.iswin local prepend_argv @@ -494,24 +492,9 @@ function module.feed_command(...) end end -local sourced_fnames = {} +-- @deprecated use nvim_exec() function module.source(code) - local fname = tmpname() - write_file(fname, code) - module.command('source '..fname) - -- DO NOT REMOVE FILE HERE. - -- do_source() has a habit of checking whether files are “same” by using inode - -- and device IDs. If you run two source() calls in quick succession there is - -- a good chance that underlying filesystem will reuse the inode, making files - -- appear as “symlinks” to do_source when it checks FileIDs. With current - -- setup linux machines (both QB, travis and mine(ZyX-I) with XFS) do reuse - -- inodes, Mac OS machines (again, both QB and travis) do not. - -- - -- Files appearing as “symlinks” mean that both the first and the second - -- source() calls will use same SID, which may fail some tests which check for - -- exact numbers after `` in e.g. function names. - sourced_fnames[#sourced_fnames + 1] = fname - return fname + module.exec(dedent(code)) end function module.has_powershell() @@ -525,7 +508,7 @@ function module.set_shell_powershell() local cmd = set_encoding..'Remove-Item -Force '..table.concat(iswin() and {'alias:cat', 'alias:echo', 'alias:sleep'} or {'alias:echo'}, ',')..';' - module.source([[ + module.exec([[ let &shell = ']]..shell..[[' set shellquote= shellxquote= let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' @@ -887,9 +870,6 @@ module = global_helpers.tbl_extend('error', module, global_helpers) return function(after_each) if after_each then after_each(function() - for _, fname in ipairs(sourced_fnames) do - os.remove(fname) - end check_logs() check_cores('build/bin/nvim') if session then diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua index dd6a9d0bde..4829a0bbe1 100644 --- a/test/functional/legacy/assert_spec.lua +++ b/test/functional/legacy/assert_spec.lua @@ -136,19 +136,19 @@ describe('assert function:', function() end) it('should have file names and passed messages', function() - local tmpname_one = source([[ + source([[ call assert_equal(1, 100, 'equal assertion failed') call assert_false('true', 'true assertion failed') call assert_true('false', 'false assertion failed') ]]) - local tmpname_two = source([[ + source([[ call assert_true('', 'file two') ]]) expected_errors({ - tmpname_one .. " line 1: equal assertion failed: Expected 1 but got 100", - tmpname_one .. " line 2: true assertion failed: Expected False but got 'true'", - tmpname_one .. " line 3: false assertion failed: Expected True but got 'false'", - tmpname_two .. " line 1: file two: Expected True but got ''", + "nvim_exec(): equal assertion failed: Expected 1 but got 100", + "nvim_exec(): true assertion failed: Expected False but got 'true'", + "nvim_exec(): false assertion failed: Expected True but got 'false'", + "nvim_exec(): file two: Expected True but got ''", }) end) end) diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index 8d25b9d927..eec89aa919 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -10,6 +10,7 @@ local source = helpers.source local poke_eventloop = helpers.poke_eventloop local uname = helpers.uname local load_adjust = helpers.load_adjust +local write_file = helpers.write_file local isCI = helpers.isCI local function isasan() @@ -84,6 +85,12 @@ setmetatable(monitor_memory_usage, end}) describe('memory usage', function() + local tmpfile = 'X_memory_usage' + + after_each(function() + os.remove(tmpfile) + end) + local function check_result(tbl, status, result) if not status then print('') @@ -103,7 +110,7 @@ describe('memory usage', function() it('function capture vargs', function() local pid = eval('getpid()') local before = monitor_memory_usage(pid) - source([[ + write_file(tmpfile, [[ func s:f(...) let x = a:000 endfunc @@ -111,6 +118,8 @@ describe('memory usage', function() call s:f(0) endfor ]]) + -- TODO: check_result fails if command() is used here. Why? #16064 + feed_command('source '..tmpfile) poke_eventloop() local after = monitor_memory_usage(pid) -- Estimate the limit of max usage as 2x initial usage. @@ -136,7 +145,7 @@ describe('memory usage', function() it('function capture lvars', function() local pid = eval('getpid()') local before = monitor_memory_usage(pid) - local fname = source([[ + write_file(tmpfile, [[ if !exists('s:defined_func') func s:f() let x = l: @@ -147,10 +156,12 @@ describe('memory usage', function() call s:f() endfor ]]) + feed_command('source '..tmpfile) poke_eventloop() local after = monitor_memory_usage(pid) for _ = 1, 3 do - feed_command('so '..fname) + -- TODO: check_result fails if command() is used here. Why? #16064 + feed_command('source '..tmpfile) poke_eventloop() end local last = monitor_memory_usage(pid) diff --git a/test/functional/vimscript/lang_spec.lua b/test/functional/vimscript/lang_spec.lua index d5254986ab..90437f2ee1 100644 --- a/test/functional/vimscript/lang_spec.lua +++ b/test/functional/vimscript/lang_spec.lua @@ -11,6 +11,7 @@ describe('vimscript', function() return end source([[ + let s:foo = 1 func! _dummy_function() echo 1 endfunc