diff options
Diffstat (limited to 'test/functional/options')
| -rw-r--r-- | test/functional/options/autochdir_spec.lua | 3 | ||||
| -rw-r--r-- | test/functional/options/defaults_spec.lua | 706 | ||||
| -rw-r--r-- | test/functional/options/fillchars_spec.lua | 73 | ||||
| -rw-r--r-- | test/functional/options/keymap_spec.lua | 233 | ||||
| -rw-r--r-- | test/functional/options/num_options_spec.lua | 110 | ||||
| -rw-r--r-- | test/functional/options/pastetoggle_spec.lua | 40 | ||||
| -rw-r--r-- | test/functional/options/shortmess_spec.lua | 97 | ||||
| -rw-r--r-- | test/functional/options/tabstop_spec.lua | 23 |
8 files changed, 1227 insertions, 58 deletions
diff --git a/test/functional/options/autochdir_spec.lua b/test/functional/options/autochdir_spec.lua index 0e293761ad..2fce0a5ed9 100644 --- a/test/functional/options/autochdir_spec.lua +++ b/test/functional/options/autochdir_spec.lua @@ -10,9 +10,10 @@ describe("'autochdir'", function() -- By default 'autochdir' is off, thus getcwd() returns the repo root. clear(targetdir..'/tty-test.c') local rootdir = getcwd() + local expected = rootdir .. '/' .. targetdir -- With 'autochdir' on, we should get the directory of tty-test.c. clear('--cmd', 'set autochdir', targetdir..'/tty-test.c') - eq(rootdir..'/'..targetdir, getcwd()) + eq(helpers.iswin() and expected:gsub('/', '\\') or expected, getcwd()) end) end) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index a36939b0bd..f6f3f02f45 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -1,80 +1,712 @@ local helpers = require('test.functional.helpers')(after_each) + local Screen = require('test.functional.ui.screen') -local eval, eq = helpers.eval, helpers.eq -local execute = helpers.execute - -local function init_session(...) - local args = { helpers.nvim_prog, '-i', 'NONE', '--embed', - '--cmd', 'set shortmess+=I background=light noswapfile noautoindent', - '--cmd', 'set laststatus=1 undodir=. directory=. viewdir=. backupdir=.' - } - for _, v in ipairs({...}) do - table.insert(args, v) - end - helpers.set_session(helpers.spawn(args)) -end + +local meths = helpers.meths +local command = helpers.command +local clear = helpers.clear +local exc_exec = helpers.exc_exec +local eval = helpers.eval +local eq = helpers.eq +local funcs = helpers.funcs +local insert = helpers.insert +local iswin = helpers.iswin +local neq = helpers.neq +local mkdir = helpers.mkdir +local rmdir = helpers.rmdir +local alter_slashes = helpers.alter_slashes describe('startup defaults', function() describe(':filetype', function() local function expect_filetype(expected) - local screen = Screen.new(48, 4) + local screen = Screen.new(50, 4) screen:attach() - execute('filetype') + command('filetype') screen:expect([[ - ^ | - ~ | - ~ | + ^ | + ~ | + ~ | ]]..expected ) end - it('enabled by `-u NORC`', function() - init_session('-u', 'NORC') + it('all ON after `-u NORC`', function() + clear('-u', 'NORC') expect_filetype( - 'filetype detection:ON plugin:ON indent:ON |') + 'filetype detection:ON plugin:ON indent:ON |') end) - it('disabled by `-u NONE`', function() - init_session('-u', 'NONE') + it('all ON after `:syntax …` #7765', function() + clear('-u', 'NORC', '--cmd', 'syntax on') expect_filetype( - 'filetype detection:OFF plugin:OFF indent:OFF |') + 'filetype detection:ON plugin:ON indent:ON |') + clear('-u', 'NORC', '--cmd', 'syntax off') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') end) - it('overridden by early `filetype on`', function() - init_session('-u', 'NORC', '--cmd', 'filetype on') + it('all OFF after `-u NONE`', function() + clear('-u', 'NONE') expect_filetype( - 'filetype detection:ON plugin:OFF indent:OFF |') + 'filetype detection:OFF plugin:OFF indent:OFF |') end) - it('overridden by early `filetype plugin on`', function() - init_session('-u', 'NORC', '--cmd', 'filetype plugin on') + it('explicit OFF stays OFF', function() + clear('-u', 'NORC', '--cmd', + 'syntax off | filetype off | filetype plugin indent off') + expect_filetype( + 'filetype detection:OFF plugin:OFF indent:OFF |') + clear('-u', 'NORC', '--cmd', 'syntax off | filetype plugin indent off') + expect_filetype( + 'filetype detection:ON plugin:OFF indent:OFF |') + clear('-u', 'NORC', '--cmd', 'filetype indent off') + expect_filetype( + 'filetype detection:ON plugin:ON indent:OFF |') + clear('-u', 'NORC', '--cmd', 'syntax off | filetype off') expect_filetype( - 'filetype detection:ON plugin:ON indent:OFF |') + 'filetype detection:OFF plugin:(on) indent:(on) |') + -- Swap the order. + clear('-u', 'NORC', '--cmd', 'filetype off | syntax off') + expect_filetype( + 'filetype detection:OFF plugin:(on) indent:(on) |') end) - it('overridden by early `filetype indent on`', function() - init_session('-u', 'NORC', '--cmd', 'filetype indent on') + it('all ON after early `:filetype … on`', function() + -- `:filetype … on` should not change the defaults. #7765 + -- Only an explicit `:filetype … off` sets OFF. + + clear('-u', 'NORC', '--cmd', 'filetype on') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') + clear('-u', 'NORC', '--cmd', 'filetype plugin on') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') + clear('-u', 'NORC', '--cmd', 'filetype indent on') expect_filetype( - 'filetype detection:ON plugin:OFF indent:ON |') + 'filetype detection:ON plugin:ON indent:ON |') + end) + + it('late `:filetype … off` stays OFF', function() + clear('-u', 'NORC', '-c', 'filetype off') + expect_filetype( + 'filetype detection:OFF plugin:(on) indent:(on) |') + clear('-u', 'NORC', '-c', 'filetype plugin off') + expect_filetype( + 'filetype detection:ON plugin:OFF indent:ON |') + clear('-u', 'NORC', '-c', 'filetype indent off') + expect_filetype( + 'filetype detection:ON plugin:ON indent:OFF |') end) end) describe('syntax', function() it('enabled by `-u NORC`', function() - init_session('-u', 'NORC') + clear('-u', 'NORC') eq(1, eval('g:syntax_on')) end) it('disabled by `-u NONE`', function() - init_session('-u', 'NONE') + clear('-u', 'NONE') eq(0, eval('exists("g:syntax_on")')) end) - it('overridden by early `syntax off`', function() - init_session('-u', 'NORC', '--cmd', 'syntax off') + it('`:syntax off` stays off', function() + -- early + clear('-u', 'NORC', '--cmd', 'syntax off') eq(0, eval('exists("g:syntax_on")')) + -- late + clear('-u', 'NORC', '-c', 'syntax off') + eq(0, eval('exists("g:syntax_on")')) + end) + + it('":if 0|syntax on|endif" does not affect default #8728', function() + clear('-u', 'NORC', '--cmd', ':if 0|syntax on|endif') + eq(1, eval('exists("g:syntax_on")')) + clear('-u', 'NORC', '--cmd', ':if 0|syntax off|endif') + eq(1, eval('exists("g:syntax_on")')) + end) + end) + + describe("'fillchars'", function() + it('vert/fold flags', function() + clear() + local screen = Screen.new(50, 5) + screen:attach() + command('set laststatus=0') + insert([[ + 1 + 2 + 3 + 4]]) + command('normal! ggjzfj') + command('vsp') + screen:expect([[ + 1 │1 | + ^+-- 2 lines: 2··········│+-- 2 lines: 2·········| + 4 │4 | + ~ │~ | + | + ]]) + + -- ambiwidth=double defaults to single-byte fillchars. + command('set ambiwidth=double') + screen:expect([[ + 1 |1 | + ^+-- 2 lines: 2----------|+-- 2 lines: 2---------| + 4 |4 | + ~ |~ | + | + ]]) + end) + end) + + describe("'packpath'", function() + it('defaults to &runtimepath', function() + eq(meths.get_option('runtimepath'), meths.get_option('packpath')) + end) + + it('does not follow modifications to runtimepath', function() + meths.command('set runtimepath+=foo') + neq(meths.get_option('runtimepath'), meths.get_option('packpath')) + meths.command('set packpath+=foo') + eq(meths.get_option('runtimepath'), meths.get_option('packpath')) + end) + end) + + it('v:progpath is set to the absolute path', function() + eq(eval("fnamemodify(v:progpath, ':p')"), eval('v:progpath')) + end) + + describe('$NVIM_LOG_FILE', function() + local datasubdir = iswin() and 'nvim-data' or 'nvim' + local xdgdir = 'Xtest-startup-xdg-logpath' + local xdgdatadir = xdgdir..'/'..datasubdir + after_each(function() + os.remove('Xtest-logpath') + rmdir(xdgdir) + end) + + it('is used if expansion succeeds', function() + clear({env={ + NVIM_LOG_FILE='Xtest-logpath', + }}) + eq('Xtest-logpath', eval('$NVIM_LOG_FILE')) + end) + it('defaults to stdpath("data")/log if empty', function() + eq(true, mkdir(xdgdir) and mkdir(xdgdatadir)) + clear({env={ + XDG_DATA_HOME=xdgdir, + NVIM_LOG_FILE='', -- Empty is invalid. + }}) + -- server_start() calls ELOG, which tickles log_path_init(). + pcall(command, 'call serverstart(serverlist()[0])') + + eq(xdgdir..'/'..datasubdir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/')) + end) + it('defaults to stdpath("data")/log if invalid', function() + eq(true, mkdir(xdgdir) and mkdir(xdgdatadir)) + clear({env={ + XDG_DATA_HOME=xdgdir, + NVIM_LOG_FILE='.', -- Any directory is invalid. + }}) + -- server_start() calls ELOG, which tickles log_path_init(). + pcall(command, 'call serverstart(serverlist()[0])') + + eq(xdgdir..'/'..datasubdir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/')) + end) + it('defaults to .nvimlog if stdpath("data") is invalid', function() + clear({env={ + XDG_DATA_HOME='Xtest-missing-xdg-dir', + NVIM_LOG_FILE='.', -- Any directory is invalid. + }}) + -- server_start() calls ELOG, which tickles log_path_init(). + pcall(command, 'call serverstart(serverlist()[0])') + + eq('.nvimlog', eval('$NVIM_LOG_FILE')) + end) + end) +end) + +describe('XDG-based defaults', function() + -- Need separate describe() blocks to not run clear() twice. + -- Do not put before_each() here for the same reasons. + + describe('with empty/broken environment', function() + it('sets correct defaults', function() + clear({env={ + XDG_CONFIG_HOME=nil, + XDG_DATA_HOME=nil, + XDG_CACHE_HOME=nil, + XDG_RUNTIME_DIR=nil, + XDG_CONFIG_DIRS=nil, + XDG_DATA_DIRS=nil, + LOCALAPPDATA=nil, + HOMEPATH=nil, + HOMEDRIVE=nil, + HOME=nil, + TEMP=nil, + VIMRUNTIME=nil, + USER=nil, + }}) + + eq('.', meths.get_option('backupdir')) + eq('.', meths.get_option('viewdir')) + eq('.', meths.get_option('directory')) + eq('.', meths.get_option('undodir')) + end) + end) + + -- TODO(jkeyes): tests below fail on win32 because of path separator. + if helpers.pending_win32(pending) then return end + + describe('with too long XDG variables', function() + before_each(function() + clear({env={ + XDG_CONFIG_HOME=('/x'):rep(4096), + XDG_CONFIG_DIRS=(('/a'):rep(2048) + .. ':' .. ('/b'):rep(2048) + .. (':/c'):rep(512)), + XDG_DATA_HOME=('/X'):rep(4096), + XDG_DATA_DIRS=(('/A'):rep(2048) + .. ':' .. ('/B'):rep(2048) + .. (':/C'):rep(512)), + }}) + end) + + it('are correctly set', function() + eq((('/x'):rep(4096) .. '/nvim' + .. ',' .. ('/a'):rep(2048) .. '/nvim' + .. ',' .. ('/b'):rep(2048) .. '/nvim' + .. (',' .. '/c/nvim'):rep(512) + .. ',' .. ('/X'):rep(4096) .. '/nvim/site' + .. ',' .. ('/A'):rep(2048) .. '/nvim/site' + .. ',' .. ('/B'):rep(2048) .. '/nvim/site' + .. (',' .. '/C/nvim/site'):rep(512) + .. ',' .. eval('$VIMRUNTIME') + .. (',' .. '/C/nvim/site/after'):rep(512) + .. ',' .. ('/B'):rep(2048) .. '/nvim/site/after' + .. ',' .. ('/A'):rep(2048) .. '/nvim/site/after' + .. ',' .. ('/X'):rep(4096) .. '/nvim/site/after' + .. (',' .. '/c/nvim/after'):rep(512) + .. ',' .. ('/b'):rep(2048) .. '/nvim/after' + .. ',' .. ('/a'):rep(2048) .. '/nvim/after' + .. ',' .. ('/x'):rep(4096) .. '/nvim/after' + ), meths.get_option('runtimepath')) + meths.command('set runtimepath&') + meths.command('set backupdir&') + meths.command('set directory&') + meths.command('set undodir&') + meths.command('set viewdir&') + eq((('/x'):rep(4096) .. '/nvim' + .. ',' .. ('/a'):rep(2048) .. '/nvim' + .. ',' .. ('/b'):rep(2048) .. '/nvim' + .. (',' .. '/c/nvim'):rep(512) + .. ',' .. ('/X'):rep(4096) .. '/nvim/site' + .. ',' .. ('/A'):rep(2048) .. '/nvim/site' + .. ',' .. ('/B'):rep(2048) .. '/nvim/site' + .. (',' .. '/C/nvim/site'):rep(512) + .. ',' .. eval('$VIMRUNTIME') + .. (',' .. '/C/nvim/site/after'):rep(512) + .. ',' .. ('/B'):rep(2048) .. '/nvim/site/after' + .. ',' .. ('/A'):rep(2048) .. '/nvim/site/after' + .. ',' .. ('/X'):rep(4096) .. '/nvim/site/after' + .. (',' .. '/c/nvim/after'):rep(512) + .. ',' .. ('/b'):rep(2048) .. '/nvim/after' + .. ',' .. ('/a'):rep(2048) .. '/nvim/after' + .. ',' .. ('/x'):rep(4096) .. '/nvim/after' + ), meths.get_option('runtimepath')) + eq('.,' .. ('/X'):rep(4096) .. '/nvim/backup', + meths.get_option('backupdir')) + eq(('/X'):rep(4096) .. '/nvim/swap//', meths.get_option('directory')) + eq(('/X'):rep(4096) .. '/nvim/undo', meths.get_option('undodir')) + eq(('/X'):rep(4096) .. '/nvim/view', meths.get_option('viewdir')) + end) + end) + + describe('with XDG variables that can be expanded', function() + before_each(function() + clear({env={ + XDG_CONFIG_HOME='$XDG_DATA_HOME', + XDG_CONFIG_DIRS='$XDG_DATA_DIRS', + XDG_DATA_HOME='$XDG_CONFIG_HOME', + XDG_DATA_DIRS='$XDG_CONFIG_DIRS', + }}) + end) + + it('are not expanded', function() + eq(('$XDG_DATA_HOME/nvim' + .. ',$XDG_DATA_DIRS/nvim' + .. ',$XDG_CONFIG_HOME/nvim/site' + .. ',$XDG_CONFIG_DIRS/nvim/site' + .. ',' .. eval('$VIMRUNTIME') + .. ',$XDG_CONFIG_DIRS/nvim/site/after' + .. ',$XDG_CONFIG_HOME/nvim/site/after' + .. ',$XDG_DATA_DIRS/nvim/after' + .. ',$XDG_DATA_HOME/nvim/after' + ), meths.get_option('runtimepath')) + meths.command('set runtimepath&') + meths.command('set backupdir&') + meths.command('set directory&') + meths.command('set undodir&') + meths.command('set viewdir&') + eq(('$XDG_DATA_HOME/nvim' + .. ',$XDG_DATA_DIRS/nvim' + .. ',$XDG_CONFIG_HOME/nvim/site' + .. ',$XDG_CONFIG_DIRS/nvim/site' + .. ',' .. eval('$VIMRUNTIME') + .. ',$XDG_CONFIG_DIRS/nvim/site/after' + .. ',$XDG_CONFIG_HOME/nvim/site/after' + .. ',$XDG_DATA_DIRS/nvim/after' + .. ',$XDG_DATA_HOME/nvim/after' + ), meths.get_option('runtimepath')) + eq('.,$XDG_CONFIG_HOME/nvim/backup', meths.get_option('backupdir')) + eq('$XDG_CONFIG_HOME/nvim/swap//', meths.get_option('directory')) + eq('$XDG_CONFIG_HOME/nvim/undo', meths.get_option('undodir')) + eq('$XDG_CONFIG_HOME/nvim/view', meths.get_option('viewdir')) + meths.command('set all&') + eq(('$XDG_DATA_HOME/nvim' + .. ',$XDG_DATA_DIRS/nvim' + .. ',$XDG_CONFIG_HOME/nvim/site' + .. ',$XDG_CONFIG_DIRS/nvim/site' + .. ',' .. eval('$VIMRUNTIME') + .. ',$XDG_CONFIG_DIRS/nvim/site/after' + .. ',$XDG_CONFIG_HOME/nvim/site/after' + .. ',$XDG_DATA_DIRS/nvim/after' + .. ',$XDG_DATA_HOME/nvim/after' + ), meths.get_option('runtimepath')) + eq('.,$XDG_CONFIG_HOME/nvim/backup', meths.get_option('backupdir')) + eq('$XDG_CONFIG_HOME/nvim/swap//', meths.get_option('directory')) + eq('$XDG_CONFIG_HOME/nvim/undo', meths.get_option('undodir')) + eq('$XDG_CONFIG_HOME/nvim/view', meths.get_option('viewdir')) + end) + end) + + describe('with commas', function() + before_each(function() + clear({env={ + XDG_CONFIG_HOME=', , ,', + XDG_CONFIG_DIRS=',-,-,:-,-,-', + XDG_DATA_HOME=',=,=,', + XDG_DATA_DIRS=',≡,≡,:≡,≡,≡', + }}) + end) + + it('are escaped properly', function() + eq(('\\, \\, \\,/nvim' + .. ',\\,-\\,-\\,/nvim' + .. ',-\\,-\\,-/nvim' + .. ',\\,=\\,=\\,/nvim/site' + .. ',\\,≡\\,≡\\,/nvim/site' + .. ',≡\\,≡\\,≡/nvim/site' + .. ',' .. eval('$VIMRUNTIME') + .. ',≡\\,≡\\,≡/nvim/site/after' + .. ',\\,≡\\,≡\\,/nvim/site/after' + .. ',\\,=\\,=\\,/nvim/site/after' + .. ',-\\,-\\,-/nvim/after' + .. ',\\,-\\,-\\,/nvim/after' + .. ',\\, \\, \\,/nvim/after' + ), meths.get_option('runtimepath')) + meths.command('set runtimepath&') + meths.command('set backupdir&') + meths.command('set directory&') + meths.command('set undodir&') + meths.command('set viewdir&') + eq(('\\, \\, \\,/nvim' + .. ',\\,-\\,-\\,/nvim' + .. ',-\\,-\\,-/nvim' + .. ',\\,=\\,=\\,/nvim/site' + .. ',\\,≡\\,≡\\,/nvim/site' + .. ',≡\\,≡\\,≡/nvim/site' + .. ',' .. eval('$VIMRUNTIME') + .. ',≡\\,≡\\,≡/nvim/site/after' + .. ',\\,≡\\,≡\\,/nvim/site/after' + .. ',\\,=\\,=\\,/nvim/site/after' + .. ',-\\,-\\,-/nvim/after' + .. ',\\,-\\,-\\,/nvim/after' + .. ',\\, \\, \\,/nvim/after' + ), meths.get_option('runtimepath')) + eq('.,\\,=\\,=\\,/nvim/backup', meths.get_option('backupdir')) + eq('\\,=\\,=\\,/nvim/swap//', meths.get_option('directory')) + eq('\\,=\\,=\\,/nvim/undo', meths.get_option('undodir')) + eq('\\,=\\,=\\,/nvim/view', meths.get_option('viewdir')) end) end) end) +describe('stdpath()', function() + -- Windows appends 'nvim-data' instead of just 'nvim' to prevent collisions + -- due to XDG_CONFIG_HOME and XDG_DATA_HOME being the same. + local datadir = iswin() and 'nvim-data' or 'nvim' + + it('acceptance', function() + clear() -- Do not explicitly set any env vars. + + eq('nvim', funcs.fnamemodify(funcs.stdpath('cache'), ':t')) + eq('nvim', funcs.fnamemodify(funcs.stdpath('config'), ':t')) + eq(datadir, funcs.fnamemodify(funcs.stdpath('data'), ':t')) + eq('table', type(funcs.stdpath('config_dirs'))) + eq('table', type(funcs.stdpath('data_dirs'))) + -- Check for crash. #8393 + eq(2, eval('1+1')) + end) + + context('returns a String', function() + + describe('with "config"' , function () + it('knows XDG_CONFIG_HOME', function() + clear({env={ + XDG_CONFIG_HOME=alter_slashes('/home/docwhat/.config'), + }}) + eq(alter_slashes('/home/docwhat/.config/nvim'), funcs.stdpath('config')) + end) + + it('handles changes during runtime', function() + clear({env={ + XDG_CONFIG_HOME=alter_slashes('/home/original'), + }}) + eq(alter_slashes('/home/original/nvim'), funcs.stdpath('config')) + command("let $XDG_CONFIG_HOME='"..alter_slashes('/home/new').."'") + eq(alter_slashes('/home/new/nvim'), funcs.stdpath('config')) + end) + + it("doesn't expand $VARIABLES", function() + clear({env={ + XDG_CONFIG_HOME='$VARIABLES', + VARIABLES='this-should-not-happen', + }}) + eq(alter_slashes('$VARIABLES/nvim'), funcs.stdpath('config')) + end) + + it("doesn't expand ~/", function() + clear({env={ + XDG_CONFIG_HOME=alter_slashes('~/frobnitz'), + }}) + eq(alter_slashes('~/frobnitz/nvim'), funcs.stdpath('config')) + end) + end) + + describe('with "data"' , function () + it('knows XDG_DATA_HOME', function() + clear({env={ + XDG_DATA_HOME=alter_slashes('/home/docwhat/.local'), + }}) + eq(alter_slashes('/home/docwhat/.local/'..datadir), funcs.stdpath('data')) + end) + + it('handles changes during runtime', function() + clear({env={ + XDG_DATA_HOME=alter_slashes('/home/original'), + }}) + eq(alter_slashes('/home/original/'..datadir), funcs.stdpath('data')) + command("let $XDG_DATA_HOME='"..alter_slashes('/home/new').."'") + eq(alter_slashes('/home/new/'..datadir), funcs.stdpath('data')) + end) + + it("doesn't expand $VARIABLES", function() + clear({env={ + XDG_DATA_HOME='$VARIABLES', + VARIABLES='this-should-not-happen', + }}) + eq(alter_slashes('$VARIABLES/'..datadir), funcs.stdpath('data')) + end) + + it("doesn't expand ~/", function() + clear({env={ + XDG_DATA_HOME=alter_slashes('~/frobnitz'), + }}) + eq(alter_slashes('~/frobnitz/'..datadir), funcs.stdpath('data')) + end) + end) + + describe('with "cache"' , function () + it('knows XDG_CACHE_HOME', function() + clear({env={ + XDG_CACHE_HOME=alter_slashes('/home/docwhat/.cache'), + }}) + eq(alter_slashes('/home/docwhat/.cache/nvim'), funcs.stdpath('cache')) + end) + + it('handles changes during runtime', function() + clear({env={ + XDG_CACHE_HOME=alter_slashes('/home/original'), + }}) + eq(alter_slashes('/home/original/nvim'), funcs.stdpath('cache')) + command("let $XDG_CACHE_HOME='"..alter_slashes('/home/new').."'") + eq(alter_slashes('/home/new/nvim'), funcs.stdpath('cache')) + end) + + it("doesn't expand $VARIABLES", function() + clear({env={ + XDG_CACHE_HOME='$VARIABLES', + VARIABLES='this-should-not-happen', + }}) + eq(alter_slashes('$VARIABLES/nvim'), funcs.stdpath('cache')) + end) + + it("doesn't expand ~/", function() + clear({env={ + XDG_CACHE_HOME=alter_slashes('~/frobnitz'), + }}) + eq(alter_slashes('~/frobnitz/nvim'), funcs.stdpath('cache')) + end) + end) + end) + + context('returns a List', function() + -- Some OS specific variables the system would have set. + local function base_env() + if iswin() then + return { + HOME='C:\\Users\\docwhat', -- technically, is not a usual PATH + HOMEDRIVE='C:', + HOMEPATH='\\Users\\docwhat', + LOCALAPPDATA='C:\\Users\\docwhat\\AppData\\Local', + TEMP='C:\\Users\\docwhat\\AppData\\Local\\Temp', + TMPDIR='C:\\Users\\docwhat\\AppData\\Local\\Temp', + TMP='C:\\Users\\docwhat\\AppData\\Local\\Temp', + } + else + return { + HOME='/home/docwhat', + HOMEDRIVE='HOMEDRIVE-should-be-ignored', + HOMEPATH='HOMEPATH-should-be-ignored', + LOCALAPPDATA='LOCALAPPDATA-should-be-ignored', + TEMP='TEMP-should-be-ignored', + TMPDIR='TMPDIR-should-be-ignored', + TMP='TMP-should-be-ignored', + } + end + end + + local function set_paths_via_system(var_name, paths) + local env = base_env() + env[var_name] = table.concat(paths, ':') + clear({env=env}) + end + + local function set_paths_at_runtime(var_name, paths) + clear({env=base_env()}) + meths.set_var('env_val', table.concat(paths, ':')) + command(('let $%s=g:env_val'):format(var_name)) + end + + local function behaves_like_dir_list_env(msg, stdpath_arg, env_var_name, paths, expected_paths) + describe(msg, function() + it('set via system', function() + set_paths_via_system(env_var_name, paths) + eq(expected_paths, funcs.stdpath(stdpath_arg)) + end) + + it('set at runtime', function() + set_paths_at_runtime(env_var_name, paths) + eq(expected_paths, funcs.stdpath(stdpath_arg)) + end) + end) + end + + describe('with "config_dirs"' , function () + behaves_like_dir_list_env( + 'handles XDG_CONFIG_DIRS with one path', + 'config_dirs', 'XDG_CONFIG_DIRS', + { + alter_slashes('/home/docwhat/.config') + }, + { + alter_slashes('/home/docwhat/.config/nvim') + }) + + behaves_like_dir_list_env( + 'handles XDG_CONFIG_DIRS with two paths', + 'config_dirs', 'XDG_CONFIG_DIRS', + { + alter_slashes('/home/docwhat/.config'), + alter_slashes('/etc/config') + }, + { + alter_slashes('/home/docwhat/.config/nvim'), + alter_slashes('/etc/config/nvim') + }) + + behaves_like_dir_list_env( + "doesn't expand $VAR and $IBLES", + 'config_dirs', 'XDG_CONFIG_DIRS', + { '$HOME', '$TMP' }, + { + alter_slashes('$HOME/nvim'), + alter_slashes('$TMP/nvim') + }) + + + behaves_like_dir_list_env( + "doesn't expand ~/", + 'config_dirs', 'XDG_CONFIG_DIRS', + { + alter_slashes('~/.oldconfig'), + alter_slashes('~/.olderconfig') + }, + { + alter_slashes('~/.oldconfig/nvim'), + alter_slashes('~/.olderconfig/nvim') + }) + end) + + describe('with "data_dirs"' , function () + behaves_like_dir_list_env( + 'knows XDG_DATA_DIRS with one path', + 'data_dirs', 'XDG_DATA_DIRS', + { + alter_slashes('/home/docwhat/.data') + }, + { + alter_slashes('/home/docwhat/.data/nvim') + }) + + behaves_like_dir_list_env( + 'knows XDG_DATA_DIRS with two paths', + 'data_dirs', 'XDG_DATA_DIRS', + { + alter_slashes('/home/docwhat/.data'), + alter_slashes('/etc/local') + }, + { + alter_slashes('/home/docwhat/.data/nvim'), + alter_slashes('/etc/local/nvim'), + }) + + behaves_like_dir_list_env( + "doesn't expand $VAR and $IBLES", + 'data_dirs', 'XDG_DATA_DIRS', + { '$HOME', '$TMP' }, + { + alter_slashes('$HOME/nvim'), + alter_slashes('$TMP/nvim') + }) + + behaves_like_dir_list_env( + "doesn't expand ~/", + 'data_dirs', 'XDG_DATA_DIRS', + { + alter_slashes('~/.oldconfig'), + alter_slashes('~/.olderconfig') + }, + { + alter_slashes('~/.oldconfig/nvim'), + alter_slashes('~/.olderconfig/nvim'), + }) + end) + end) + + describe('errors', function() + it('on unknown strings', function() + eq('Vim(call):E6100: "capybara" is not a valid stdpath', exc_exec('call stdpath("capybara")')) + eq('Vim(call):E6100: "" is not a valid stdpath', exc_exec('call stdpath("")')) + eq('Vim(call):E6100: "23" is not a valid stdpath', exc_exec('call stdpath(23)')) + end) + + it('on non-strings', function() + eq('Vim(call):E731: using Dictionary as a String', exc_exec('call stdpath({"eris": 23})')) + eq('Vim(call):E730: using List as a String', exc_exec('call stdpath([23])')) + end) + end) +end) diff --git a/test/functional/options/fillchars_spec.lua b/test/functional/options/fillchars_spec.lua new file mode 100644 index 0000000000..99177a11b4 --- /dev/null +++ b/test/functional/options/fillchars_spec.lua @@ -0,0 +1,73 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear, command = helpers.clear, helpers.command +local eval = helpers.eval +local eq = helpers.eq +local exc_exec = helpers.exc_exec + +describe("'fillchars'", function() + local screen + + before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + local function shouldfail(val,errval) + errval = errval or val + eq('Vim(set):E474: Invalid argument: fillchars='..errval, + exc_exec('set fillchars='..val)) + end + + describe('"eob" flag', function() + it("uses '~' by default", function() + eq('', eval('&fillchars')) + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + | + ]]) + end) + it('supports whitespace', function() + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + | + ]]) + command('set fillchars=eob:\\ ') + screen:expect([[ + ^ | + | + | + | + | + ]]) + end) + it('supports multibyte char', function() + command('set fillchars=eob:ñ') + screen:expect([[ + ^ | + ñ | + ñ | + ñ | + | + ]]) + end) + it('handles invalid values', function() + shouldfail('eob:') -- empty string + shouldfail('eob:馬') -- doublewidth char + shouldfail('eob:å̲') -- composing chars + shouldfail('eob:xy') -- two ascii chars + shouldfail('eob:\255', 'eob:<ff>') -- invalid UTF-8 + end) + end) +end) diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua new file mode 100644 index 0000000000..7f6d623dc7 --- /dev/null +++ b/test/functional/options/keymap_spec.lua @@ -0,0 +1,233 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, feed, eq = helpers.clear, helpers.feed, helpers.eq +local expect, command, eval = helpers.expect, helpers.command, helpers.eval +local insert, call = helpers.insert, helpers.call +local funcs, dedent = helpers.funcs, helpers.dedent + +-- First test it's implemented using the :lmap and :lnoremap commands, then +-- check those mappings behave as expected. +describe("'keymap' / :lmap", function() + clear() + before_each(function() + clear() + insert("lllaaa") + command('set iminsert=1') + command('set imsearch=1') + command('lmap l a') + feed('gg0') + end) + + describe("'keymap' as :lmap", function() + -- Shows that 'keymap' sets language mappings that allows remapping. + -- This equivalence allows us to only test :lmap commands and assert they + -- behave the same as 'keymap' settings. + -- It does rely on the absence of special code that implements 'keymap' + -- and :lmap differently but shows mappings from the 'keymap' after + -- typing :lmap. + -- At the moment this is the case. + it("'keymap' mappings are shown with :lmap", function() + command('lmapclear') + command('lmapclear <buffer>') + command('set keymap=dvorak') + command('set nomore') + local bindings = funcs.nvim_command_output('lmap') + eq(dedent([[ + + l " @_ + l ' @- + l + @} + l , @w + l - @[ + l . @v + l / @z + l : @S + l ; @s + l < @W + l = @] + l > @V + l ? @Z + l A @A + l B @X + l C @J + l D @E + l E @> + l F @U + l G @I + l H @D + l I @C + l J @H + l K @T + l L @N + l M @M + l N @B + l O @R + l P @L + l Q @" + l R @P + l S @O + l T @Y + l U @G + l V @K + l W @< + l X @Q + l Y @F + l Z @: + l [ @/ + l \ @\ + l ] @= + l _ @{ + l a @a + l b @x + l c @j + l d @e + l e @. + l f @u + l g @i + l h @d + l i @c + l j @h + l k @t + l l @n + l m @m + l n @b + l o @r + l p @l + l q @' + l r @p + l s @o + l t @y + l u @g + l v @k + l w @, + l x @q + l y @f + l z @; + l { @? + l | @| + l } @+]]), bindings) + end) + end) + describe("'iminsert' option", function() + it("Uses :lmap in insert mode when ON", function() + feed('il<esc>') + expect('alllaaa') + end) + it("Ignores :lmap in insert mode when OFF", function() + command('set iminsert=0') + feed('il<esc>') + expect('llllaaa') + end) + it("Can be toggled with <C-^> in insert mode", function() + feed('i<C-^>l<C-^>l<esc>') + expect('lalllaaa') + eq(eval('&iminsert'), 1) + feed('i<C-^><esc>') + eq(eval('&iminsert'), 0) + end) + end) + describe("'imsearch' option", function() + it("Uses :lmap at search prompt when ON", function() + feed('/lll<cr>3x') + expect('lll') + end) + it("Ignores :lmap at search prompt when OFF", function() + command('set imsearch=0') + feed('gg/lll<cr>3x') + expect('aaa') + end) + it("Can be toggled with C-^", function() + eq(eval('&imsearch'), 1) + feed('/<C-^>lll<cr>3x') + expect('aaa') + eq(eval('&imsearch'), 0) + feed('u0/<C-^>lll<cr>3x') + expect('lll') + eq(eval('&imsearch'), 1) + end) + it("can follow 'iminsert'", function() + command('set imsearch=-1') + feed('/lll<cr>3x') + expect('lll') + eq(eval('&imsearch'), -1) + eq(eval('&iminsert'), 1) + feed('u/<C-^>lll<cr>3x') + expect('aaa') + eq(eval('&imsearch'), -1) + eq(eval('&iminsert'), 0) + end) + end) + it(":lmap not applied to macros", function() + command("call setreg('a', 'il')") + feed('@a') + expect('llllaaa') + eq(call('getreg', 'a'), 'il') + end) + it(":lmap applied to macro recording", function() + feed('qail<esc>q@a') + expect('aalllaaa') + eq(call('getreg', 'a'), 'ia') + end) + it(":lmap not applied to mappings", function() + command('imap t l') + feed('it<esc>') + expect('llllaaa') + end) + it("mappings applied to keys created with :lmap", function() + command('imap a x') + feed('il<esc>') + expect('xlllaaa') + end) + it("mappings not applied to keys gotten with :lnoremap", function() + command('lmapclear') + command('lnoremap l a') + command('imap a x') + feed('il<esc>') + expect('alllaaa') + end) + -- This is a problem introduced when introducting :lmap and macro + -- compatibility. There are no plans to fix this as the complexity involved + -- seems too great. + pending('mappings not applied to macro replay of :lnoremap', function() + command('lmapclear') + command('lnoremap l a') + command('imap a x') + feed('qail<esc>q') + expect('alllaaa') + feed('@a') + expect('aalllaaa') + end) + it("is applied when using f/F t/T", function() + feed('flx') + expect('lllaa') + feed('0ia<esc>4lFlx') + expect('lllaa') + feed('tllx') + expect('llla') + feed('0ia<esc>4lTlhx') + expect('llla') + end) + it('takes priority over :imap mappings', function() + command('imap l x') + feed('il<esc>') + expect('alllaaa') + command('lmapclear') + command('lmap l a') + feed('il') + expect('aalllaaa') + end) + it('does not cause recursive mappings', function() + command('lmap a l') + feed('qaila<esc>q') + expect('allllaaa') + feed('u@a') + expect('allllaaa') + end) + it('can handle multicharacter mappings', function() + command("lmap 'a x") + command("lmap '' '") + feed("qai'a''a<esc>q") + expect("x'alllaaa") + feed('u@a') + expect("x'alllaaa") + end) +end) diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua new file mode 100644 index 0000000000..fb0559054d --- /dev/null +++ b/test/functional/options/num_options_spec.lua @@ -0,0 +1,110 @@ +-- Tests for :setlocal and :setglobal + +local helpers = require('test.functional.helpers')(after_each) +local clear, feed_command, eval, eq, meths = + helpers.clear, helpers.feed_command, helpers.eval, helpers.eq, helpers.meths + +local function should_fail(opt, value, errmsg) + feed_command('setglobal ' .. opt .. '=' .. value) + eq(errmsg, eval("v:errmsg"):match("E%d*")) + feed_command('let v:errmsg = ""') + feed_command('setlocal ' .. opt .. '=' .. value) + eq(errmsg, eval("v:errmsg"):match("E%d*")) + feed_command('let v:errmsg = ""') + local status, err = pcall(meths.set_option, opt, value) + eq(status, false) + eq(errmsg, err:match("E%d*")) + eq('', eval("v:errmsg")) +end + +local function should_succeed(opt, value) + feed_command('setglobal ' .. opt .. '=' .. value) + feed_command('setlocal ' .. opt .. '=' .. value) + meths.set_option(opt, value) + eq(value, meths.get_option(opt)) + eq('', eval("v:errmsg")) +end + +describe(':setlocal', function() + before_each(clear) + + it('setlocal sets only local value', function() + eq(0, meths.get_option('iminsert')) + feed_command('setlocal iminsert=1') + eq(0, meths.get_option('iminsert')) + eq(0, meths.get_option('imsearch')) + feed_command('setlocal imsearch=1') + eq(0, meths.get_option('imsearch')) + end) +end) + +describe(':set validation', function() + before_each(clear) + + it('setlocal and setglobal validate values', function() + should_fail('shiftwidth', -10, 'E487') + should_succeed('shiftwidth', 0) + should_fail('tabstop', -10, 'E487') + should_fail('winheight', -10, 'E487') + should_fail('winheight', 0, 'E487') + should_fail('winminheight', -1, 'E487') + should_succeed('winminheight', 0) + should_fail('winwidth', 0, 'E487') + should_fail('helpheight', -1, 'E487') + should_fail('iminsert', 3, 'E474') + should_fail('imsearch', 3, 'E474') + should_fail('titlelen', -1, 'E487') + should_fail('cmdheight', 0, 'E487') + should_fail('updatecount', -1, 'E487') + should_fail('textwidth', -1, 'E487') + should_fail('tabstop', 0, 'E487') + should_fail('timeoutlen', -1, 'E487') + should_fail('history', 1000000, 'E474') + should_fail('regexpengine', -1, 'E474') + should_fail('regexpengine', 3, 'E474') + should_succeed('regexpengine', 2) + should_fail('report', -1, 'E487') + should_succeed('report', 0) + should_fail('scrolloff', -1, 'E49') + should_fail('sidescrolloff', -1, 'E487') + should_fail('sidescroll', -1, 'E487') + should_fail('cmdwinheight', 0, 'E487') + should_fail('updatetime', -1, 'E487') + + should_fail('foldlevel', -5, 'E487') + should_fail('foldcolumn', 13, 'E474') + should_fail('conceallevel', 4, 'E474') + should_fail('numberwidth', 11, 'E474') + should_fail('numberwidth', 0, 'E487') + + -- If smaller than 1 this one is set to 'lines'-1 + feed_command('setglobal window=-10') + meths.set_option('window', -10) + eq(23, meths.get_option('window')) + eq('', eval("v:errmsg")) + end) + + it('set wmh/wh wmw/wiw checks', function() + feed_command('set winheight=2') + feed_command('set winminheight=3') + eq('E591', eval("v:errmsg"):match("E%d*")) + + feed_command('set winwidth=2') + feed_command('set winminwidth=3') + eq('E592', eval("v:errmsg"):match("E%d*")) + end) + + it('set maxcombine resets to 6', function() + local function setto(value) + feed_command('setglobal maxcombine=' .. value) + feed_command('setlocal maxcombine=' .. value) + meths.set_option('maxcombine', value) + eq(6, meths.get_option('maxcombine')) + eq('', eval("v:errmsg")) + end + setto(0) + setto(1) + setto(6) + setto(7) + end) +end) diff --git a/test/functional/options/pastetoggle_spec.lua b/test/functional/options/pastetoggle_spec.lua new file mode 100644 index 0000000000..a1f86f73ff --- /dev/null +++ b/test/functional/options/pastetoggle_spec.lua @@ -0,0 +1,40 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local feed = helpers.feed +local command = helpers.command +local eq = helpers.eq +local eval = helpers.eval +local sleep = helpers.sleep +local expect = helpers.expect + +describe("'pastetoggle' option", function() + before_each(function() + clear() + command('set nopaste') + end) + + it("toggles 'paste'", function() + command('set pastetoggle=a') + eq(0, eval('&paste')) + feed('a') + -- Need another key so that the vgetorpeek() function returns. + feed('j') + eq(1, eval('&paste')) + end) + + + it('does not wait for timeout', function() + command('set pastetoggle=abc') + command('set ttimeoutlen=9999999') + eq(0, eval('&paste')) + -- n.b. need <esc> to return from vgetorpeek() + feed('abc<esc>') + eq(1, eval('&paste')) + feed('ab') + sleep(10) + feed('c<esc>') + expect('bc') + eq(1, eval('&paste')) + end) +end) diff --git a/test/functional/options/shortmess_spec.lua b/test/functional/options/shortmess_spec.lua index d531e47e28..8ea9a19464 100644 --- a/test/functional/options/shortmess_spec.lua +++ b/test/functional/options/shortmess_spec.lua @@ -1,39 +1,96 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local clear, execute = helpers.clear, helpers.execute +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed describe("'shortmess'", function() local screen before_each(function() clear() - screen = Screen.new(25, 5) + screen = Screen.new(42, 5) screen:attach() end) - after_each(function() - screen:detach() - end) - describe('"F" flag', function() - it('hides messages about the files read', function() - execute('e test') + it('hides :edit fileinfo messages', function() + command('set hidden') + command('set shortmess-=F') + feed(':edit foo<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "foo" [New File] | + ]]) + eq(1, eval('bufnr("%")')) + + command('set shortmess+=F') + feed(':edit bar<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + :edit bar | + ]]) + eq(2, eval('bufnr("%")')) + end) + + it('hides :bnext, :bprevious fileinfo messages', function() + command('set hidden') + command('set shortmess-=F') + feed(':edit foo<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "foo" [New File] | + ]]) + eq(1, eval('bufnr("%")')) + feed(':edit bar<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "bar" [New File] | + ]]) + eq(2, eval('bufnr("%")')) + feed(':bprevious<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "foo" [New file] --No lines in buffer-- | + ]]) + eq(1, eval('bufnr("%")')) + + command('set shortmess+=F') + feed(':bnext<CR>') screen:expect([[ - ^ | - ~ | - ~ | - ~ | - "test" is a directory | + ^ | + ~ | + ~ | + ~ | + :bnext | ]]) - execute('set shortmess=F') - execute('e test') + eq(2, eval('bufnr("%")')) + feed(':bprevious<CR>') screen:expect([[ - ^ | - ~ | - ~ | - ~ | - :e test | + ^ | + ~ | + ~ | + ~ | + :bprevious | ]]) + eq(1, eval('bufnr("%")')) end) end) end) diff --git a/test/functional/options/tabstop_spec.lua b/test/functional/options/tabstop_spec.lua new file mode 100644 index 0000000000..dc3ba38438 --- /dev/null +++ b/test/functional/options/tabstop_spec.lua @@ -0,0 +1,23 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local feed = helpers.feed +local eq = helpers.eq +local eval = helpers.eval + +describe("'tabstop' option", function() + before_each(function() + clear() + end) + + -- NOTE: Setting 'tabstop' to a big number reproduces crash #2838. + -- Disallowing big 'tabstop' would not fix #2838, only hide it. + it("tabstop=<big-number> does not crash #2838", function() + -- Insert a <Tab> character for 'tabstop' to work with. + feed('i<Tab><Esc>') + -- Set 'tabstop' to a very high value. + -- Use feed(), not command(), to provoke crash. + feed(':set tabstop=3000000000<CR>') + eq(2, eval("1+1")) -- Still alive? + end) +end) |