Skip to content

Commit 3c45ab9

Browse files
authored
fix: wrap command in new command format in python.nvim
* fix(commands): account for missing last arg in wrap_cursor * chore: adjust Python command description * fix: support range for `:Python` for treesitter wrap_cursor * doc: update vim doc * test(treesitter): add test for treesitter wrap cursor commands * chore: run format * test: text action and enumerate ts test * chore: lint fix test_text_actions * test: check for treesitter grammar before running tests
1 parent c9043ba commit 3c45ab9

6 files changed

Lines changed: 188 additions & 33 deletions

File tree

doc/python.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,8 @@ Parameters ~
295295
Class ~
296296
{PythonStateVEnv}
297297
Fields ~
298-
{python_interpreter} `(string | nil)`
299-
{venv_path} `(string | nil)`
298+
{python_interpreter} `(string)`
299+
{venv_path} `(string)`
300300
{install_method} `(string)`
301301
{install_file} `(string)`
302302
{source} `(string)`
@@ -320,14 +320,14 @@ Fields ~
320320
{dap} `(table<string, dap.Configuration>)`
321321

322322
------------------------------------------------------------------------------
323-
*PythonState.State()*
324-
`PythonState.State`()
323+
*PythonStateM.State()*
324+
`PythonStateM.State`()
325325
Return ~
326326
`(PythonState)`
327327

328328
------------------------------------------------------------------------------
329-
*PythonState.save()*
330-
`PythonState.save`({new_state})
329+
*PythonStateM.save()*
330+
`PythonStateM.save`({new_state})
331331
Parameters ~
332332
{new_state} `(PythonState)`
333333

doc/tags

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ PythonConfig.setup() python.txt /*PythonConfig.setup()*
88
PythonDap.prepare_debugpy() python.txt /*PythonDap.prepare_debugpy()*
99
PythonLSPCommands.pyright_change_type_checking() python.txt /*PythonLSPCommands.pyright_change_type_checking()*
1010
PythonState python.txt /*PythonState*
11-
PythonState.State() python.txt /*PythonState.State()*
12-
PythonState.save() python.txt /*PythonState.save()*
1311
PythonStateDap python.txt /*PythonStateDap*
12+
PythonStateM.State() python.txt /*PythonStateM.State()*
13+
PythonStateM.save() python.txt /*PythonStateM.save()*
1414
PythonStateVEnv python.txt /*PythonStateVEnv*
1515
PythonTreeSitterCommands.ts_wrap_at_cursor() python.txt /*PythonTreeSitterCommands.ts_wrap_at_cursor()*
1616
PythonUI.activate_system_call_ui() python.txt /*PythonUI.activate_system_call_ui()*

lua/python/commands.lua

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,12 @@ local subcommand_tbl = {
199199
end
200200

201201
if command == "wrap_cursor" then
202-
ts_cmd.ts_wrap_at_cursor(args[#args])
202+
-- Account for if the last argument is the command or an actual arg
203+
local wrap_arg = args[#args]
204+
if wrap_arg == "wrap_cursor" then
205+
wrap_arg = ""
206+
end
207+
ts_cmd.ts_wrap_at_cursor(wrap_arg)
203208
return
204209
end
205210
end,
@@ -289,7 +294,7 @@ function PythonCommands.load_commands()
289294
-- NOTE: the options will vary, based on your use case.
290295
vim.api.nvim_create_user_command("Python", python_cmd, {
291296
nargs = "+",
292-
desc = "My awesome command with subcommand completions",
297+
desc = "Python.nvim commands",
293298
complete = function(arg_lead, cmdline, _)
294299
-- Get the subcommand.
295300
local subcmd_key, subcmd_arg_lead = cmdline:match("^['<,'>]*Python[!]*%s(%S+)%s(.*)$")
@@ -310,6 +315,7 @@ function PythonCommands.load_commands()
310315
end
311316
end,
312317
bang = true, -- If you want to support ! modifiers
318+
range = true, -- Support some visual command
313319
})
314320
end
315321

scripts/minimal_init.lua

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,67 @@
11
-- Add current directory to 'runtimepath' to be able to use 'lua' files
22
vim.cmd([[let &rtp.=','.getcwd()]])
33

4-
-- Set up 'mini.test' only when calling headless Neovim (like with `make test`)
5-
if #vim.api.nvim_list_uis() == 0 then
6-
-- Add 'mini.nvim' to 'runtimepath' to be able to use 'mini.test'
7-
-- Assumed that 'mini.nvim' is stored in 'deps/mini.nvim'
8-
--
9-
local runtime_dependencies = {
10-
"deps/mini.nvim",
11-
"deps/nvim-treesitter",
12-
"deps/neotest",
13-
"deps/neotest-python",
14-
"deps/nvim-dap",
15-
"deps/nvim-dap-python",
16-
"deps/nvim-lspconfig",
17-
"deps/LuaSnip",
18-
}
19-
local runtime_path = vim.fn.join(runtime_dependencies, ",")
20-
vim.cmd("set rtp+=" .. runtime_path)
4+
local runtime_dependencies = {
5+
"deps/mini.nvim",
6+
"deps/nvim-treesitter",
7+
"deps/neotest",
8+
"deps/neotest-python",
9+
"deps/nvim-dap",
10+
"deps/nvim-dap-python",
11+
"deps/nvim-lspconfig",
12+
"deps/LuaSnip",
13+
}
14+
local runtime_path = vim.fn.join(runtime_dependencies, ",")
15+
vim.cmd("set rtp+=" .. runtime_path)
2116

22-
-- Set up 'mini.test'
23-
require("luasnip.extras.fmt")
24-
require("luasnip.nodes.absolute_indexer")
25-
require("nvim-treesitter.locals")
26-
require("mini.test").setup()
27-
require("mini.doc").setup()
17+
-- Set up 'mini.test'
18+
require("luasnip.extras.fmt")
19+
require("luasnip.nodes.absolute_indexer")
20+
require("nvim-treesitter.locals")
21+
require("nvim-treesitter").setup()
22+
require("mini.test").setup()
23+
require("mini.doc").setup()
24+
local ts_configs = require("nvim-treesitter.configs").setup({
25+
modules = {
26+
"highlight",
27+
},
28+
sync_install = false,
29+
auto_install = true,
30+
ignore_install = {},
31+
ensure_installed = {},
32+
highlight = {
33+
enable = true,
34+
},
35+
})
36+
37+
-- Clean path for use in a prefix comparison
38+
---@param input string
39+
---@return string
40+
local function clean_path(input)
41+
local pth = vim.fn.fnamemodify(input, ":p")
42+
if vim.fn.has("win32") == 1 then
43+
pth = pth:gsub("/", "\\")
44+
end
45+
return pth
46+
end
47+
48+
local function ts_is_installed(lang)
49+
local matched_parsers = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true) or {}
50+
local configs = require("nvim-treesitter.configs")
51+
local install_dir = configs.get_parser_install_dir()
52+
if not install_dir then
53+
return false
54+
end
55+
install_dir = clean_path(install_dir)
56+
for _, path in ipairs(matched_parsers) do
57+
local abspath = clean_path(path)
58+
if vim.startswith(abspath, install_dir) then
59+
return true
60+
end
61+
end
62+
return false
63+
end
64+
65+
if not ts_is_installed("python") then
66+
vim.cmd("TSInstallSync python")
2867
end

tests/test_text_actions.lua

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
-- Define helper aliases
2+
local new_set = MiniTest.new_set
3+
local expect, eq = MiniTest.expect, MiniTest.expect.equality
4+
5+
-- Create (but not start) child Neovim object
6+
local child = MiniTest.new_child_neovim()
7+
8+
-- Define main test set of this file
9+
local T = new_set({
10+
-- Register hooks
11+
hooks = {
12+
-- This will be executed before every (even nested) case
13+
pre_case = function()
14+
-- Restart child process with custom 'init.lua' script
15+
child.restart({ "-u", "scripts/minimal_init.lua" })
16+
-- Load tested plugin
17+
child.lua([[require('python').setup()]])
18+
end,
19+
-- This will be executed one after all tests from this set are finished
20+
post_once = child.stop,
21+
},
22+
})
23+
24+
local get_lines = function()
25+
return child.api.nvim_buf_get_lines(0, 0, -1, true)
26+
end
27+
28+
T["text_actions"] = MiniTest.new_set({
29+
hooks = {
30+
pre_case = function()
31+
child.cmd("e _not_existing_new_buffer.py")
32+
end,
33+
},
34+
})
35+
36+
T["text_actions"]["insert_f_string"] = function()
37+
child.type_keys("i", [[print("{foo}")]], "<left><esc>")
38+
39+
eq(get_lines(), { [[print(f"{foo}")]] })
40+
end
41+
42+
-- Return test set which will be collected and execute inside `MiniTest.run()`
43+
return T

tests/test_treesitter.lua

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
-- Define helper aliases
2+
local new_set = MiniTest.new_set
3+
local expect, eq = MiniTest.expect, MiniTest.expect.equality
4+
5+
-- Create (but not start) child Neovim object
6+
local child = MiniTest.new_child_neovim()
7+
8+
-- Define main test set of this file
9+
local T = new_set({
10+
-- Register hooks
11+
hooks = {
12+
-- This will be executed before every (even nested) case
13+
pre_case = function()
14+
-- Restart child process with custom 'init.lua' script
15+
child.restart({ "-u", "scripts/minimal_init.lua" })
16+
-- Load tested plugin
17+
child.lua([[require('python').setup()]])
18+
end,
19+
-- This will be executed one after all tests from this set are finished
20+
post_once = child.stop,
21+
},
22+
})
23+
24+
local get_lines = function()
25+
return child.api.nvim_buf_get_lines(0, 0, -1, true)
26+
end
27+
28+
T["wrap_cursor"] = MiniTest.new_set({
29+
hooks = {
30+
pre_case = function()
31+
child.cmd("e _not_existing_new_buffer.py")
32+
child.type_keys("cc", [["TEST"]], "<Esc>", "0")
33+
end,
34+
},
35+
})
36+
37+
T["wrap_cursor"]["normal"] = function()
38+
child.cmd("Python treesitter wrap_cursor test(%s)")
39+
eq(get_lines(), { [[test("TEST")]] })
40+
end
41+
42+
T["wrap_cursor"]["visual"] = function()
43+
child.type_keys("l", "v", "$")
44+
child.type_keys([[:Python treesitter wrap_cursor test(%s)<cr>]])
45+
eq(get_lines(), { [["test(TEST")]] })
46+
end
47+
48+
T["wrap_cursor"]["visual_with_selection"] = function()
49+
child.type_keys("l", "v", "$")
50+
child.type_keys([[:Python treesitter wrap_cursor<cr>1<cr>]])
51+
eq(get_lines(), { [["print(TEST")]] })
52+
end
53+
54+
T["wrap_cursor"]["with_selection"] = function()
55+
child.type_keys([[:Python treesitter wrap_cursor<cr>1<cr>]])
56+
eq(get_lines(), { [[print("TEST")]] })
57+
end
58+
59+
T["enumerate"] = function()
60+
child.cmd("e _not_existing_new_buffer.py")
61+
child.type_keys("cc", "for i in [1, 2, 3]:", "<Esc>", "0")
62+
child.type_keys([[:Python treesitter toggle_enumerate<cr>]])
63+
eq(get_lines(), { "for idx, i in enumerate([1, 2, 3]):" })
64+
end
65+
66+
-- Return test set which will be collected and execute inside `MiniTest.run()`
67+
return T

0 commit comments

Comments
 (0)