diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1f85c98bec..7adcf2579e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -258,10 +258,12 @@ jobs: version: 0.16.0 - run: sudo apt-get install -y inotify-tools + - run: mkdir -p build/ # build/ is used for shared log paths, see global ENV defs - run: zig build $OPTS test_nlua0 - run: zig build $OPTS nvim_bin && ./zig-out/bin/nvim --version - run: zig build $OPTS unittest - run: zig build $OPTS functionaltest + - run: zig build $OPTS oldtest # `zig build` uses a lua script for doctags in order to support cross-compiling # compare with the builtin generator that they match - run: cd runtime; ../zig-out/bin/nvim -u NONE -i NONE -e --headless -c "helptags ++t doc" -c quit @@ -271,6 +273,8 @@ jobs: runs-on: macos-15 timeout-minutes: 45 name: build using zig build (macos 15) + env: + OPTS: # empty steps: - uses: actions/checkout@v6 with: @@ -280,9 +284,11 @@ jobs: with: version: 0.16.0 - - run: zig build test_nlua0 -Dluajit=false - - run: zig build nvim_bin -Dluajit=false && ./zig-out/bin/nvim --version - - run: zig build functionaltest -Dluajit=false + - run: mkdir -p build/ # build/ is used for shared log paths, see global ENV defs + - run: zig build $OPTS test_nlua0 + - run: zig build $OPTS nvim_bin && ./zig-out/bin/nvim --version + - run: zig build $OPTS functionaltest + - run: zig build $OPTS oldtest zig-build-windows: runs-on: windows-2025 @@ -297,6 +303,7 @@ jobs: with: version: 0.16.0 + - run: mkdir -p build/ # build/ is used for shared log paths, see global ENV defs - run: zig build test_nlua0 - run: zig build nvim_bin - run: ./zig-out/bin/nvim --version diff --git a/build.zig.zon b/build.zig.zon index fd5326dac6..938546f673 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -6,8 +6,8 @@ .dependencies = .{ .zlua = .{ - .url = "git+https://github.com/natecraddock/ziglua#8f271c82baa5fc43aa02a72f6da020c2025d9436", - .hash = "zlua-0.1.0-hGRpC2aABQD4D9PBVH3wAW8k32-I4969MRQ0CpOwoley", + .url = "git+https://github.com/natecraddock/ziglua#1dcb9f2b7b466a1609cc11e746c2efc4f17685da", + .hash = "zlua-0.1.0-hGRpC22ABQA4y12Uz7QaHuCkKrpaa_66xidApjtFuKFg", }, .lpeg = .{ .url = "https://github.com/neovim/deps/raw/d495ee6f79e7962a53ad79670cb92488abe0b9b4/opt/lpeg-1.1.0.tar.gz", diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 5a982deb0d..8e63750dd2 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -97,12 +97,12 @@ function M.get_parser(buf, lang, opts) if not api.nvim_buf_is_loaded(buf) then return nil, string.format('Buffer %s must be loaded to create parser', buf) end - local parser = vim.npcall(M._create_parser, buf, lang, opts) - if not parser then - return nil, - string.format('Parser could not be created for buffer %s and language "%s"', buf, lang) + local status, res = pcall(M._create_parser, buf, lang, opts) + if not status then + local msg = 'Parser could not be created for buffer %s and language "%s": %s' + return nil, string.format(msg, buf, lang, res) end - parsers[buf] = parser + parsers[buf] = res end parsers[buf]:register_cbs(opts.buf_attach_cbs) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bc1342d4f5..c0339e9e1d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -212,9 +212,7 @@ void eval_clear(void) { evalvars_clear(); free_scriptnames(); // must come after evalvars_clear(). -# ifdef HAVE_WORKING_LIBINTL free_locales(); -# endif // autoloaded script names free_autoload_scriptnames(); diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 81efaa7f1a..39a369babc 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -338,8 +338,15 @@ char *get_locales(expand_T *xp, int idx) void lang_init(void) { -#if defined(__APPLE__) && !defined(ZIG_BUILD) +#if defined(__APPLE__) if (!os_env_exists("LANG", true)) { +# if defined(ZIG_BUILD) + // fuck it just use the objectively correct values: + os_setenv("LANG", "C.UTF-8", true); + setlocale(LC_ALL, "C.UTF-8"); + // Make sure strtod() uses a decimal point, not a comma. + setlocale(LC_NUMERIC, "C"); +# else char buf[50] = { 0 }; // $LANG is not set, either because it was unset or Nvim was started @@ -358,6 +365,7 @@ void lang_init(void) } else { ELOG("$LANG is empty and the macOS primary language cannot be inferred."); } +# endif } #endif } diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index aa7565fd75..c3774cf293 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1789,7 +1789,7 @@ static idx_T read_tree_node(FILE *fd, uint8_t *byts, idx_T *idxs, int maxidx, id c = (getc(fd) << 16) + c; // } if (c & WF_AFX) { - c = (getc(fd) << 24) + c; // + c = (int)((unsigned)getc(fd) << 24) + c; // } } diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 4c7305e2a9..0a9a6ce9c0 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -77,9 +77,7 @@ static const char *command_complete[] = { [EXPAND_HIGHLIGHT] = "highlight", [EXPAND_HISTORY] = "history", [EXPAND_KEYMAP] = "keymap", -#ifdef HAVE_WORKING_LIBINTL [EXPAND_LOCALES] = "locale", -#endif [EXPAND_LUA] = "lua", [EXPAND_MAPCLEAR] = "mapclear", [EXPAND_MAPPINGS] = "mapping", diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index f01530a24b..96305a611a 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -19,7 +19,10 @@ describe('treesitter language API', function() return vim.treesitter.get_parser(0, 'borklang') end) eq(NIL, parser) - eq('Parser could not be created for buffer 1 and language "borklang"', error) + matches( + '^Parser could not be created for buffer 1 and language "borklang": .+: No parser for language "borklang"$', + error + ) -- actual message depends on platform matches( @@ -110,7 +113,10 @@ describe('treesitter language API', function() return vim.treesitter.get_parser(0, 'borklang') end) eq(NIL, parser) - eq('Parser could not be created for buffer 1 and language "borklang"', error) + matches( + '^Parser could not be created for buffer 1 and language "borklang": .+: No parser for language "borklang"$', + error + ) end ) diff --git a/test/old/runner.lua b/test/old/runner.lua new file mode 100644 index 0000000000..e8558cc39a --- /dev/null +++ b/test/old/runner.lua @@ -0,0 +1,160 @@ +-- execute old style tests + +-- when compared to the testdir/Makefile based approach, this replaces +-- all wrapper process logic: +-- Makefile +-- runnvim.sh and its library test.sh +-- runnvim.vim (wrapper which runs a nested vim inside a terminal) + +-- it DOES NOT not replace the logic running inside the nvim-under-test +-- runtest.vim and its libraries setup.vim, shared.vim, check.vim + +local nvim_bin = vim.v.progpath -- must be absolute + +vim.api.nvim_set_current_dir('test/old/testdir/') + +local function readable(fn) + return vim.fn.filereadable(fn) ~= 0 +end + +local errlog = 'test.log' +local messages = 'messages' +local starttime = 'starttime' +local gen_opt_log = 'gen_opt_test.log' + +function oksystem(args) + local res = vim.system(args, {}):wait() + if res.code ~= 0 then + error(vim.inspect(args) .. ' failed:\n' .. res.stdout .. res.stderr) + end + return res +end + +function clean_output() + for _, f in ipairs { errlog, messages, starttime, gen_opt_log, 'opt_test.vim' } do + vim.uv.fs_unlink(f) + end + -- check status, some tests mess with perms so this could fail: + oksystem { 'bash', '-c', 'rm -rf X* viminfo test.out test.ok' } +end + +function if_nonempty(rawtext, name) + text = vim.trim(rawtext) + if #text > 0 then + if name then + print('== ' .. name) + end + print(text) + print(name and '== END ' .. name or '\n') + end +end + +function if_exists(fn, name) + local fil = io.open(fn) + if fil then + if_nonempty(fil:read '*a', name) + fil:close() + return true + end +end + +local verbose = vim.env.VERBOSE + +function run_nvim(...) + local cmd = vim.system( + { nvim_bin, '-u', 'NONE', '-i', 'NONE', '--headless', ... }, + { env = { VIMRUNTIME = '../../../runtime' } } + ) + return cmd:wait() +end + +function run_test(testfile) + if not vim.endswith(testfile, '.vim') then + error 'not an oldtest' + end + + clean_output() + + if testfile == 'test_options_all.vim' then + local res = run_nvim( + '-S', + 'gen_opt_test.vim', + '../../../src/nvim/options.lua', + '../../../runtime/doc/options.txt' + ) + + if if_exists(gen_opt_log, 'GEN opt_test.vim') or res.code > 0 then + return false + end + end + + local resfile = string.sub(testfile, 1, -4) .. 'res' + vim.uv.fs_unlink(resfile) + assert(not readable(resfile)) + + local opts = 'set shortmess-=F backupdir=. undodir=. viewdir=.' + local res = run_nvim('--cmd', opts, '-S', 'runtest.vim', testfile) + local passed = (readable(resfile) and res.code == 0) + + if_exists(messages) + + if verbose or not passed then + if_nonempty(res.stdout, 'STDOUT') + if_nonempty(res.stderr, 'STDERR') + -- if_exists(starttime, "starttime") + if_exists(errlog, 'test.log') + end + return passed +end + +local function all_tests() + local nested_tests = {} + local alot = io.open('test_alot.vim') + local sourcepat = '^source (test_.*%.vim)$' + for line in alot:lines() do + local m = string.match(line, sourcepat) + if m then + nested_tests[m] = true + end + end + alot:close() + + local files = {} + for name, type in vim.fs.dir('.') do + match = string.match(name, '^test_.*%.vim$') + if match then + if nested_tests[match] then + nested_tests[match] = nil + elseif match == 'test_largefile.vim' then + -- ignored: uses too much resources to run on CI + else + table.insert(files, match) + end + end + end + return files +end + +local files +if #arg > 0 then + files = {} + for _, a in ipairs(arg) do + table.insert(files, string.match(a, '[^/]*$')) + end +else + files = all_tests() +end + +local count = #files +local failures = {} +for i, fil in ipairs(files) do + print('run: ', fil) + if not run_test(fil) then + table.insert(failures, fil) + end +end + +if #failures > 0 then + print('failed:', vim.inspect(failures)) + vim.cmd 'cquit' +end diff --git a/test/run_tests.zig b/test/run_tests.zig index 39238b4473..1c864e8b79 100644 --- a/test/run_tests.zig +++ b/test/run_tests.zig @@ -50,6 +50,19 @@ pub fn test_steps(b: *std.Build, nvim_bin: *std.Build.Step.Compile, depend_on: * const functionaltest_step = b.step("functionaltest", "run functional tests"); functionaltest_step.dependOn(&functional_tests.step); + const old_tests = b.addRunArtifact(nvim_bin); + old_tests.addArg("-l"); + old_tests.addFileArg(b.path("./test/old/runner.lua")); + if (b.args) |args| { + old_tests.addArgs(args); // accept TEST_FILE as a positional argument + } + const env = old_tests.getEnvMap(); + try env.put("BUILD_DIR", b.install_path); + + const oldtest_step = b.step("oldtest", "run old tests"); + oldtest_step.dependOn(&old_tests.step); + oldtest_step.dependOn(depend_on); + if (unit_paths) |paths| { const unit_tests = try testStep(b, "unit", nvim_bin, config_dir, paths); unit_tests.step.dependOn(depend_on);