Skip to content

Commit 4435518

Browse files
feat! implement condition objects which implement various operators + tests
Works by - providing a factory `make_condition` which wraps functions as condition objects - providing two modules `conditions.show` and `condition.expand` which contain a collection of common conditions already wrapped as condition objects Weird might be the decision to use `%` as `==`-operator. This way chosen because we can`t use `__eq` as this automatically converts the return value to a boolean (we need to return condition objects). So we decided to use something which make ones head scratch and look it up in the docs (or simply don't use it) Note: one still can continue to use the `expand_conditions` module (just rediects to the `conditions/expand` module) for backwards compatibility. This is deprecated though.
1 parent 8f8d493 commit 4435518

File tree

6 files changed

+357
-13
lines changed

6 files changed

+357
-13
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
local cond_obj = require("luasnip.extras.conditions")
2+
3+
-- use the functions from show as basis and extend/overwrite functions specific for expand here
4+
local M = vim.deepcopy(require("luasnip.extras.conditions.show"))
5+
-----------------------
6+
-- PRESET CONDITIONS --
7+
-----------------------
8+
local function line_begin(line_to_cursor, matched_trigger)
9+
-- +1 because `string.sub("abcd", 1, -2)` -> abc
10+
return line_to_cursor:sub(1, -(#matched_trigger + 1)):match("^%s*$")
11+
end
12+
M.line_begin = cond_obj.make_condition(line_begin)
13+
14+
return M
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
local M = {}
2+
3+
-----------------------
4+
-- CONDITION OBJECTS --
5+
-----------------------
6+
local condition_mt = {
7+
-- logic operators
8+
-- not '-'
9+
__unm = function(o1)
10+
return M.make_condition(function(...)
11+
return not o1(...)
12+
end)
13+
end,
14+
-- or '+'
15+
__add = function(o1, o2)
16+
return M.make_condition(function(...)
17+
return o1(...) or o2(...)
18+
end)
19+
end,
20+
-- and '*'
21+
__mul = function(o1, o2)
22+
return M.make_condition(function(...)
23+
return o1(...) and o2(...)
24+
end)
25+
end,
26+
-- xor '^'
27+
__pow = function(o1, o2)
28+
return M.make_condition(function(...)
29+
return o1(...) ~= o2(...)
30+
end)
31+
end,
32+
-- xnor '%'
33+
-- might be counter intuitive, but as we can't use '==' (must return bool)
34+
-- it's best to use something weird (doesn't have to be used)
35+
__mod = function(o1, o2)
36+
return function(...)
37+
return o1(...) == o2(...)
38+
end
39+
end,
40+
-- use table like a function by overloading __call
41+
__call = function(tab, line_to_cursor, matched_trigger, captures)
42+
return tab.func(line_to_cursor, matched_trigger, captures)
43+
end,
44+
}
45+
function M.make_condition(func)
46+
return setmetatable({ func = func }, condition_mt)
47+
end
48+
49+
return M
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
local cond_obj = require("luasnip.extras.conditions")
2+
3+
local M = {}
4+
-----------------------
5+
-- PRESET CONDITIONS --
6+
-----------------------
7+
local function line_end(line_to_cursor)
8+
local line = vim.api.nvim_get_current_line()
9+
return #line_to_cursor == #line
10+
end
11+
M.line_end = cond_obj.make_condition(line_end)
12+
13+
return M
Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1 @@
1-
local M = {}
2-
3-
function M.line_begin(line_to_cursor, matched_trigger)
4-
-- +1 because `string.sub("abcd", 1, -2)` -> abc
5-
return line_to_cursor:sub(1, -(#matched_trigger + 1)):match("^%s*$")
6-
end
7-
8-
function M.line_end(line_to_cursor)
9-
local line = vim.api.nvim_get_current_line()
10-
return #line_to_cursor == #line
11-
end
12-
13-
return M
1+
return require("luasnip.extras.conditions.expand")

tests/unit/conditions_spec.lua

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
local helpers = require("test.functional.helpers")(after_each)
2+
local ls_helpers = require("helpers")
3+
4+
describe("expand_conditions", function()
5+
before_each(function()
6+
helpers.clear()
7+
ls_helpers.session_setup_luasnip()
8+
helpers.exec_lua("noop = function() end")
9+
end)
10+
11+
-- apparently clear() needs to run before anything else...
12+
helpers.clear()
13+
helpers.exec("set rtp+=" .. os.getenv("LUASNIP_SOURCE"))
14+
it("simple", function()
15+
local function foo()
16+
return helpers.exec_lua([[
17+
local mkcond = require("luasnip.extras.conditions").make_condition
18+
local c = mkcond(function() return true end)
19+
return c() == true
20+
]])
21+
end
22+
assert.has_no.errors(foo)
23+
assert.is_true(foo())
24+
end)
25+
describe("logic ops", function()
26+
describe("and", function()
27+
local function foo(b1, b2)
28+
-- Attention: use this only when testing (otherwise (pot.
29+
-- malicious) users might inject code)
30+
return helpers.exec_lua(
31+
([[
32+
local mkcond = require("luasnip.extras.conditions").make_condition
33+
local c = mkcond(function() return %s end) * mkcond(function() return %s end)
34+
return c() == %s
35+
]]):format(
36+
tostring(b1),
37+
tostring(b2),
38+
tostring(b1 and b2)
39+
)
40+
)
41+
end
42+
for _, ele in ipairs({
43+
{ true, true },
44+
{ true, false },
45+
{ false, true },
46+
{ false, false },
47+
}) do
48+
it(
49+
("%s and %s"):format(tostring(ele[1]), tostring(ele[2])),
50+
function()
51+
local test = function()
52+
return foo(ele[1], ele[2])
53+
end
54+
assert.has_no.errors(test)
55+
assert.is_true(test())
56+
end
57+
)
58+
end
59+
end)
60+
describe("or", function()
61+
local function foo(b1, b2)
62+
-- Attention: use this only when testing (otherwise (pot.
63+
-- malicious) users might inject code)
64+
return helpers.exec_lua(([[
65+
local mkcond = require("luasnip.extras.conditions").make_condition
66+
local c = mkcond(function() return %s end) + mkcond(function() return %s end)
67+
return c() == %s
68+
]]):format(tostring(b1), tostring(b2), tostring(b1 or b2)))
69+
end
70+
for _, ele in ipairs({
71+
{ true, true },
72+
{ true, false },
73+
{ false, true },
74+
{ false, false },
75+
}) do
76+
it(
77+
("%s or %s"):format(tostring(ele[1]), tostring(ele[2])),
78+
function()
79+
local test = function()
80+
return foo(ele[1], ele[2])
81+
end
82+
assert.has_no.errors(test)
83+
assert.is_true(test())
84+
end
85+
)
86+
end
87+
end)
88+
describe("xor", function()
89+
local function foo(b1, b2)
90+
-- Attention: use this only when testing (otherwise (pot.
91+
-- malicious) users might inject code)
92+
return helpers.exec_lua(
93+
([[
94+
local mkcond = require("luasnip.extras.conditions").make_condition
95+
local c = mkcond(function() return %s end) ^ mkcond(function() return %s end)
96+
return c() == %s
97+
]]):format(
98+
tostring(b1),
99+
tostring(b2),
100+
tostring((b1 and not b2) or (not b1 and b2))
101+
)
102+
)
103+
end
104+
for _, ele in ipairs({
105+
{ true, true },
106+
{ true, false },
107+
{ false, true },
108+
{ false, false },
109+
}) do
110+
it(
111+
("%s xor %s"):format(tostring(ele[1]), tostring(ele[2])),
112+
function()
113+
local test = function()
114+
return foo(ele[1], ele[2])
115+
end
116+
assert.has_no.errors(test)
117+
assert.is_true(test())
118+
end
119+
)
120+
end
121+
end)
122+
describe("xnor", function()
123+
local function foo(b1, b2)
124+
-- Attention: use this only when testing (otherwise (pot.
125+
-- malicious) users might inject code)
126+
return helpers.exec_lua(([[
127+
local mkcond = require("luasnip.extras.conditions").make_condition
128+
local c = mkcond(function() return %s end) %% mkcond(function() return %s end)
129+
return c() == %s
130+
]]):format(tostring(b1), tostring(b2), tostring(b1 == b2)))
131+
end
132+
for _, ele in ipairs({
133+
{ true, true },
134+
{ true, false },
135+
{ false, true },
136+
{ false, false },
137+
}) do
138+
it(
139+
("%s xnor %s"):format(tostring(ele[1]), tostring(ele[2])),
140+
function()
141+
local test = function()
142+
return foo(ele[1], ele[2])
143+
end
144+
assert.has_no.errors(test)
145+
assert.is_true(test())
146+
end
147+
)
148+
end
149+
end)
150+
describe("not", function()
151+
local function foo(b1)
152+
-- Attention: use this only when testing (otherwise (pot.
153+
-- malicious) users might inject code)
154+
return helpers.exec_lua(([[
155+
local mkcond = require("luasnip.extras.conditions").make_condition
156+
local c = -mkcond(function() return %s end)
157+
return c() == %s
158+
]]):format(tostring(b1), tostring(not b1)))
159+
end
160+
for _, ele in ipairs({ { true }, { false } }) do
161+
it(("not %s"):format(tostring(ele[1])), function()
162+
local test = function()
163+
return foo(ele[1])
164+
end
165+
assert.has_no.errors(test)
166+
assert.is_true(test())
167+
end)
168+
end
169+
end)
170+
describe("composite", function()
171+
local function foo(b1, b2, b3)
172+
-- Attention: use this only when testing (otherwise (pot.
173+
-- malicious) users might inject code)
174+
return helpers.exec_lua(
175+
([[
176+
local mkcond = require("luasnip.extras.conditions").make_condition
177+
local c = - ( mkcond(function() return %s end) + mkcond(function() return %s end) * mkcond(function() return %s end)) ^ ( mkcond(function() return %s end) + mkcond(function() return %s end) * mkcond(function() return %s end))
178+
return c() == %s
179+
]]):format(
180+
tostring(b1),
181+
tostring(b2),
182+
tostring(b3),
183+
tostring(b3),
184+
tostring(b1),
185+
tostring(b2),
186+
tostring(not (b1 or b2 and b3) ~= (b3 or b1 and b2))
187+
)
188+
)
189+
end
190+
for _, ele in ipairs({
191+
{ true, true, true },
192+
{ true, true, false },
193+
{ true, false, true },
194+
{ true, false, false },
195+
{ false, true, true },
196+
{ false, true, false },
197+
{ false, false, true },
198+
{ false, false, false },
199+
}) do
200+
it(
201+
("composite %s %s %s"):format(
202+
tostring(ele[1]),
203+
tostring(ele[2]),
204+
tostring(ele[3])
205+
),
206+
function()
207+
local test = function()
208+
return foo(ele[1], ele[2], ele[3])
209+
end
210+
assert.has_no.errors(test)
211+
assert.is_true(test())
212+
end
213+
)
214+
end
215+
end)
216+
end)
217+
describe("line_begin", function()
218+
it("is at begin", function()
219+
local function foo()
220+
return helpers.exec_lua([[
221+
local conds = require("luasnip.extras.expand_conditions")
222+
local c = conds.line_begin
223+
return not c("hello world", "hello world") ~= true -- allow nil/object
224+
]])
225+
end
226+
assert.has_no.errors(foo)
227+
assert.is_true(foo())
228+
end)
229+
it("is NOT at begin", function()
230+
local function foo()
231+
return helpers.exec_lua([[
232+
local conds = require("luasnip.extras.expand_conditions")
233+
local c = conds.line_begin
234+
return not c("hello world", "ld") ~= false -- allow nil/object
235+
]])
236+
end
237+
assert.has_no.errors(foo)
238+
assert.is_true(foo())
239+
end)
240+
end)
241+
describe("line_end", function()
242+
it("is at begin", function()
243+
local function foo()
244+
return helpers.exec_lua([[
245+
local vim_bak = vim
246+
-- vim.api.nvim_get_current_line
247+
vim = {api = {nvim_get_current_line = function() return "hello world ending" end}}
248+
local conds = require("luasnip.extras.expand_conditions")
249+
local c = conds.line_end
250+
local ret = not c("hello world ending") ~= true -- allow nil/object
251+
vim = vim_bak
252+
return ret
253+
]])
254+
end
255+
assert.has_no.errors(foo)
256+
assert.is_true(foo())
257+
end)
258+
it("is NOT at begin", function()
259+
local function foo()
260+
return helpers.exec_lua([[
261+
local vim_bak = vim
262+
-- vim.api.nvim_get_current_line
263+
vim = {api = {nvim_get_current_line = function() return "hello world ending" end}}
264+
local conds = require("luasnip.extras.expand_conditions")
265+
local c = conds.line_end
266+
local ret = not c("hello world") ~= false -- allow nil/object
267+
vim = vim_bak
268+
return ret
269+
]])
270+
end
271+
assert.has_no.errors(foo)
272+
assert.is_true(foo())
273+
end)
274+
end)
275+
end)

tests/unit/extend_decorator_spec.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ local exec = helpers.exec
44
local ls_helpers = require("helpers")
55

66
describe("luasnip.util.extend_decorator", function()
7+
before_each(function()
8+
helpers.clear()
9+
ls_helpers.session_setup_luasnip()
10+
helpers.exec_lua("noop = function() end")
11+
end)
712
local shared_setup1 = [[
813
local function passthrough(arg1, arg2)
914
return arg1, arg2

0 commit comments

Comments
 (0)