diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a36246a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: default + +on: [push, pull_request] + +jobs: + run_tests: + name: unit tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + neovim_version: ['v0.8.3', 'v0.9.5', 'v0.10.2', 'nightly'] + include: + - os: macos-latest + neovim_version: v0.10.1 + - os: windows-latest + neovim_version: v0.10.1 + steps: + - uses: actions/checkout@v4 + - name: Setup neovim + uses: rhysd/action-setup-vim@v1 + with: + neovim: true + version: ${{ matrix.neovim_version }} + - name: Run tests + timeout-minutes: 3 + run: make test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4c0f4dc --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +test: deps/mini.nvim + nvim --headless --noplugin -u ./scripts/minimal_init.lua -c "lua MiniTest.run()" + +# Run test from file at `$FILE` environment variable +test_file: deps/mini.nvim + nvim --headless --noplugin -u ./scripts/minimal_init.lua -c "lua MiniTest.run_file('$(FILE)')" + +# Download 'mini.nvim' to use its 'mini.test' testing module +deps/mini.nvim: + @mkdir -p deps + git clone --filter=blob:none https://github.com/echasnovski/mini.nvim $@ diff --git a/lua/scratch/utils.lua b/lua/scratch/utils.lua index 6546d87..4c21e7e 100644 --- a/lua/scratch/utils.lua +++ b/lua/scratch/utils.lua @@ -77,17 +77,29 @@ local table_length = function(T) end ---@return string[] -local function getSelectedText() - local _, csrow, cscol, _ = unpack(vim.fn.getpos("'<")) - local _, cerow, cecol, _ = unpack(vim.fn.getpos("'>")) - - local lines = vim.fn.getline(csrow, cerow) - local n = table_length(lines) - if n <= 0 then - return {} +local function getSelectedText(mark, selection_mode) + local pos1 = vim.fn.getpos("v") + local pos2 = vim.fn.getpos(mark) + local lines = {} + local start_row, start_col, end_row, end_col = pos1[2], pos1[3], pos2[2], pos2[3] + local text = vim.api.nvim_buf_get_lines(0, start_row - 1, end_row, true) + end_row = end_row - start_row + 1 + start_row = 1 + if selection_mode == "v" then + table.insert(lines, text[1]:sub(start_col)) + for i = start_row + 1, end_row do + table.insert(lines, text[i]) + end + lines[end_row] = lines[end_row]:sub(1, end_col) + elseif selection_mode == "V" then + for i = start_row, end_row do + table.insert(lines, text[i]) + end + elseif selection_mode == vim.api.nvim_replace_termcodes("", true, true, true) then + for i = start_row, end_row do + table.insert(lines, text[i]:sub(start_col, end_col)) + end end - lines[n] = string.sub(lines[n], 1, cecol) - lines[1] = string.sub(lines[1], cscol) return lines end diff --git a/plugin/scratch_plugin.lua b/plugin/scratch_plugin.lua index b79c4c4..eacd90a 100644 --- a/plugin/scratch_plugin.lua +++ b/plugin/scratch_plugin.lua @@ -16,11 +16,14 @@ local scratch_main = require("scratch") scratch_main.setup() vim.api.nvim_create_user_command("Scratch", function(args) - if args.range > 0 then - scratch_api.scratch({ content = utils.getSelectedText() }) - else - scratch_api.scratch() + local mode = vim.api.nvim_get_mode().mode + local opts + if mode ~= "n" then + opts = { content = utils.getSelectedText(".", mode) } + elseif args.range > 0 then + opts = { content = vim.api.nvim_buf_get_lines(0, args.line1 - 1, args.line2, true) } end + scratch_api.scratch(opts) end, { range = true }) vim.api.nvim_create_user_command("ScratchOpen", scratch_api.openScratch, {}) diff --git a/scripts/minimal_init.lua b/scripts/minimal_init.lua new file mode 100644 index 0000000..7af7ee3 --- /dev/null +++ b/scripts/minimal_init.lua @@ -0,0 +1,12 @@ +-- Add current directory to 'runtimepath' to be able to use 'lua' files +vim.cmd([[let &rtp.=','.getcwd()]]) + +-- Set up 'mini.test' only when calling headless Neovim (like with `make test`) +if #vim.api.nvim_list_uis() == 0 then + -- Add 'mini.nvim' to 'runtimepath' to be able to use 'mini.test' + -- Assumed that 'mini.nvim' is stored in 'deps/mini.nvim' + vim.cmd("set rtp+=deps/mini.nvim") + + -- Set up 'mini.test' + require("mini.test").setup() +end diff --git a/tests/test_plugin.lua b/tests/test_plugin.lua new file mode 100644 index 0000000..427e070 --- /dev/null +++ b/tests/test_plugin.lua @@ -0,0 +1,135 @@ +local child = MiniTest.new_child_neovim() +local new_set = MiniTest.new_set + +local function mock() + local mock_api = {} + mock_api.scratch = function(opts) + _G.scratch_opts = opts + end + mock_api.openScratch = function() + print("TODO") + end + mock_api.fzfScratch = mock_api.openScratch + mock_api.scratchWithName = mock_api.openScratch + + package.loaded["scratch.api"] = mock_api + vim.g.loaded_scratch = 0 + dofile("plugin/scratch_plugin.lua") +end +local select_wise = function(coord, selection_mode) + local vim = child + local start_row, start_col, end_row, end_col = coord[1], coord[2], coord[3], coord[4] + local mode = vim.api.nvim_get_mode() + if mode.mode ~= "n" then + local esc = vim.api.nvim_replace_termcodes("", true, true, true) + vim.api.nvim_cmd({ cmd = "normal", bang = true, args = { esc } }, {}) + vim.api.nvim_cmd({ cmd = "normal", bang = true, args = { esc } }, {}) + end + selection_mode = vim.api.nvim_replace_termcodes(selection_mode, true, true, true) + vim.api.nvim_cmd({ cmd = "normal", bang = true, args = { selection_mode } }, {}) + + vim.api.nvim_win_set_cursor(0, { start_row, start_col - 1 }) + vim.cmd("normal! o") + vim.api.nvim_win_set_cursor(0, { end_row, end_col - 1 }) +end + +local function table_select(text, coord, selection_mode) + local lines = {} + local start_row, start_col, end_row, end_col = coord[1], coord[2], coord[3], coord[4] + if selection_mode == "v" then + table.insert(lines, text[start_row]:sub(start_col)) + for i = start_row + 1, end_row do + table.insert(lines, text[i]) + end + local ind = end_row - start_row + 1 + lines[ind] = lines[ind]:sub(1, end_col) + elseif selection_mode == "V" then + for i = start_row, end_row do + table.insert(lines, text[i]) + end + elseif selection_mode == vim.api.nvim_replace_termcodes("", true, true, true) then + for i = start_row, end_row do + table.insert(lines, text[i]:sub(start_col, end_col)) + end + end + return lines +end +local T = new_set() +T["Scratch"] = new_set({ + hooks = { + pre_once = function() + child.restart({ "-u", "scripts/minimal_init.lua" }) + child.lua_func(mock) + end, + post_once = child.stop, + }, +}) +local BUFFER_TEXT = { + "some", + "text here will be", + "inserted", + "now", +} +T["Scratch"]["select_branch"] = new_set({ + parametrize = { { "v" }, { "V" }, { vim.api.nvim_replace_termcodes("", true, true, true) } }, +}) +T["Scratch"]["select_branch"]["parameter"] = new_set({ + parametrize = { + { + { 1, 1, 1, 4 }, + }, + { + { 1, 2, 1, 4 }, + }, + { + { 2, 1, 3, 4 }, + }, + { + { 1, 1, 2, 2 }, + }, + { + { 1, 1, 1, 1 }, + }, + { + { 1, 1, 4, 4 }, + }, + }, +}) +T["Scratch"]["select_branch"]["parameter"]["some_text"] = function(selection_mode, coord) + child.api.nvim_buf_set_lines(0, 0, -1, false, BUFFER_TEXT) + child.api.nvim_set_keymap("v", " ", ":Scratch", {}) + select_wise(coord, selection_mode) + MiniTest.add_note("|1>" .. vim.inspect(child.api.nvim_get_mode())) + MiniTest.add_note( + "|2>" + .. vim.inspect( + child.lua_get( + [[require("scratch.utils").getSelectedText(".", vim.api.nvim_get_mode().mode)]] + ) + ) + ) + child.type_keys(" ") + MiniTest.expect.equality( + table_select(BUFFER_TEXT, coord, selection_mode), + child.lua_get("_G.scratch_opts").content + ) +end +T["Scratch"]["range_branch"] = new_set() +T["Scratch"]["range_branch"]["lines"] = function() + child.api.nvim_buf_set_lines(0, 0, -1, false, BUFFER_TEXT) + child.type_keys(":1,2Scratch") + MiniTest.expect.equality( + table_select(BUFFER_TEXT, { 1, 1, 2, 2 }, "V"), + child.lua_get("_G.scratch_opts").content + ) +end +T["Scratch"]["range_branch"]["selection"] = function() + child.api.nvim_buf_set_lines(0, 0, -1, false, BUFFER_TEXT) + select_wise({ 1, 1, 3, 3 }, "V") + child.type_keys(":", "Scratch") + MiniTest.expect.equality( + table_select(BUFFER_TEXT, { 1, 1, 3, 2 }, "V"), + child.lua_get("_G.scratch_opts").content + ) +end +return T diff --git a/tests/test_select.lua b/tests/test_select.lua new file mode 100644 index 0000000..15d9dd6 --- /dev/null +++ b/tests/test_select.lua @@ -0,0 +1,168 @@ +local child = MiniTest.new_child_neovim() +local getSelectedText +local function table_select(text, coord, selection_mode) + local lines = {} + local start_row, start_col, end_row, end_col = coord[1], coord[2], coord[3], coord[4] + if selection_mode == "v" then + table.insert(lines, text[start_row]:sub(start_col)) + for i = start_row + 1, end_row do + table.insert(lines, text[i]) + end + local ind = end_row - start_row + 1 + lines[ind] = lines[ind]:sub(1, end_col) + elseif selection_mode == "V" then + for i = start_row, end_row do + table.insert(lines, text[i]) + end + elseif selection_mode == vim.api.nvim_replace_termcodes("", true, true, true) then + for i = start_row, end_row do + table.insert(lines, text[i]:sub(start_col, end_col)) + end + end + return lines +end +local select_wise = function(coord, selection_mode) + local vim = child + local start_row, start_col, end_row, end_col = coord[1], coord[2], coord[3], coord[4] + local mode = vim.api.nvim_get_mode() + if mode.mode ~= "n" then + local esc = vim.api.nvim_replace_termcodes("", true, true, true) + vim.api.nvim_cmd({ cmd = "normal", bang = true, args = { esc } }, {}) + vim.api.nvim_cmd({ cmd = "normal", bang = true, args = { esc } }, {}) + end + selection_mode = vim.api.nvim_replace_termcodes(selection_mode, true, true, true) + vim.api.nvim_cmd({ cmd = "normal", bang = true, args = { selection_mode } }, {}) + + vim.api.nvim_win_set_cursor(0, { start_row, start_col - 1 }) + vim.cmd("normal! o") + vim.api.nvim_win_set_cursor(0, { end_row, end_col - 1 }) +end + +-- local function old_real(mark, mode) +-- local _, csrow, cscol, _ = unpack(vim.fn.getpos("'<")) +-- local _, cerow, cecol, _ = unpack(vim.fn.getpos("'>")) +-- +-- local lines = vim.fn.getline(csrow, cerow) +-- local n = #lines +-- if n <= 0 then +-- return {} +-- end +-- lines[n] = string.sub(lines[n], 1, cecol) +-- lines[1] = string.sub(lines[1], cscol) +-- return lines +-- end + +local new_set = MiniTest.new_set +local T = new_set({ + parametrize = { { "v" }, { "V" }, { vim.api.nvim_replace_termcodes("", true, true, true) } }, +}) +T["param"] = new_set({ + parametrize = { + { + { 1, 1, 1, 4 }, + }, + { + { 1, 2, 1, 4 }, + }, + { + { 2, 1, 3, 4 }, + }, + { + { 1, 1, 2, 2 }, + }, + { + { 1, 1, 1, 1 }, + }, + { + { 1, 1, 4, 4 }, + }, + }, +}) +local BUFFER_TEXT = { + "some", + "text here will be", + "inserted", + "now", +} +-- T["param"]["old"] = new_set({ +-- hooks = { +-- pre_case = function() +-- child.restart({ "-u", "scripts/minimal_init.lua" }) +-- child.api.nvim_buf_set_lines(0, 0, -1, false, BUFFER_TEXT) +-- end, +-- post_case = function() +-- child.stop() +-- end, +-- }, +-- }) +T["param"]["new"] = new_set({ + hooks = { + pre_case = function() + child.restart({ "-u", "scripts/minimal_init.lua" }) + child.api.nvim_buf_set_lines(0, 0, -1, false, BUFFER_TEXT) + getSelectedText = require("scratch.utils").getSelectedText + end, + post_case = function() + child.stop() + end, + }, + n_retry = 2, +}) +-- T["param"]["old"]["not_workd"] = function(selection_mode, coord) +-- select_wise(coord, selection_mode) +-- MiniTest.expect.no_equality( +-- table_select(BUFFER_TEXT, coord, selection_mode), +-- child.lua_func(old_real) +-- ) +-- end +T["param"]["new"]["workd"] = function(selection_mode, coord) + select_wise(coord, selection_mode) + MiniTest.expect.equality( + table_select(BUFFER_TEXT, coord, selection_mode), + child.lua_func(getSelectedText, ".", selection_mode) + ) +end +-- T["param"]["old"]["at_command"] = function(selection_mode, coord) +-- select_wise(coord, selection_mode) +-- child.type_keys(":") +-- MiniTest.expect.equality( +-- table_select(BUFFER_TEXT, coord, selection_mode), +-- child.lua_func(old_real) +-- ) +-- end +T["param"]["new"]["at_command"] = function(selection_mode, coord) + select_wise(coord, selection_mode) + child.type_keys(":") + MiniTest.add_note("|1>--- " .. vim.inspect(child.fn.getpos("'>"))) + MiniTest.add_note("|2>--- " .. vim.inspect(child.fn.getpos("."))) + MiniTest.expect.equality( + table_select(BUFFER_TEXT, coord, selection_mode), + child.lua_func(getSelectedText, "'>", selection_mode) + ) +end +T["new"] = new_set({ + hooks = { + pre_case = function() + child.restart({ "-u", "scripts/minimal_init.lua" }) + child.api.nvim_buf_set_lines(0, 0, -1, false, {}) + getSelectedText = require("scratch.utils").getSelectedText + end, + post_case = function() + child.stop() + end, + }, + parametrize = { { { 1, 1, 1, 1 } } }, +}) +T["new"]["empty_file"] = function(selection_mode, coord) + select_wise(coord, selection_mode) + local pos1 = child.fn.getpos("v") + local pos2 = child.fn.getpos(".") + MiniTest.add_note("|1>--- " .. vim.inspect(pos1)) + MiniTest.add_note("|2>--- " .. vim.inspect(pos2)) + MiniTest.expect.equality( + table_select({ "" }, coord, selection_mode), + child.lua_func(getSelectedText, ".", selection_mode) + ) +end + +return T