Skip to content

Commit

Permalink
Add LS_CAPTURE_n and LS_TRIGGER environment variables
Browse files Browse the repository at this point in the history
Breaking change: LS is now a builtin namespace so user defined
environments can't use it any more, here we will add any other
builtin we find useful in the future.
  • Loading branch information
leiserfg committed Sep 2, 2022
1 parent 1843932 commit c919d04
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 28 deletions.
13 changes: 9 additions & 4 deletions DOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -1399,18 +1399,21 @@ If `jsregexp` is not available, transformation are replaced by a simple copy.
# VARIABLES

All `TM_something`-variables are supported with two additions:
`SELECT_RAW` and `SELECT_DEDENT`. These were introduced because
`LS_SELECT_RAW` and `LS_SELECT_DEDENT`. These were introduced because
`TM_SELECTED_TEXT` is designed to be compatible with vscodes' behavior, which
can be counterintuitive when the snippet can be expanded at places other than
the point where selection started (or when doing transformations on selected text).
Besides those we also provide `LS_TRIGGER` which contains the trigger of the snippet,
and `LS_CAPTURE_n` (where n is a positive integer) that contains the n-th capture
when using a regex with capture groups as `trig` in the snippet definition.

All variables can be used outside of lsp-parsed snippets as their values are
stored in a snippets' `snip.env`-table:
```lua
s("selected_text", f(function(args, snip)
local res, env = {}, snip.env
table.insert(res, "Selected Text (current line is " .. env.TM_LINE_NUMBER .. "):")
for _, ele in ipairs(env.SELECT_RAW) do table.insert(res, ele) end
for _, ele in ipairs(env.LS_SELECT_RAW) do table.insert(res, ele) end
return res
end, {}))
```
Expand All @@ -1436,9 +1439,11 @@ You can also add your own variables by using the `ls.env_namespace(name, opts)`
Is a function that receives a string and returns a value for the var with that name
or a table from var name to a value
(in this case, if the value is a function it will be executed lazily once per snippet expansion).
* `init`: `fn(pos: pair[int])->map[string, EnvVal]` Takes the 0-based position of the cursor and returns
* `init`: `fn(info: table)->map[string, EnvVal]` Returns
a table of variables that will set to the environment of the snippet on expansion,
use this for vars that have to be calculated in that moment or that depend on each other.
The `info` table argument contains `pos` (0-based position of the cursor on expansion),
the `trigger` of the snippet and the `captures` list.
* `eager`: `list[string]` names of variables that will be taken from `vars` and appended eagerly (like those in init)
* `multiline_vars`: `(fn(name:string)->bool)|map[sting, bool]|bool|string[]` Says if certain vars are a table or just a string,
can be a function that get's the name of the var and returns true if the var is a key,
Expand All @@ -1464,7 +1469,7 @@ ls.env_namespace("SYS", {vars=os.getenv, eager={"HOME"}})

-- then you can use $SYS_HOME which was eagerly initialized but also $SYS_USER (or any other system environment var) in your snippets

lsp.env_namespace("POS", {init=function(pos) return {"VAL": vim.inspect(pos)}} end)
lsp.env_namespace("POS", {init=function(info) return {"VAL": vim.inspect(info.pos)}} end)

-- then you can use $POS_VAL in your snippets

Expand Down
24 changes: 15 additions & 9 deletions doc/luasnip.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*luasnip.txt* For NVIM v0.5.0 Last change: 2022 August 30
*luasnip.txt* For NVIM v0.5.0 Last change: 2022 September 02

==============================================================================
Table of Contents *luasnip-table-of-contents*
Expand Down Expand Up @@ -1340,11 +1340,15 @@ If `jsregexp` is not available, transformation are replaced by a simple copy.
==============================================================================
17. VARIABLES *luasnip-variables*

All `TM_something`-variables are supported with two additions: `SELECT_RAW` and
`SELECT_DEDENT`. These were introduced because `TM_SELECTED_TEXT` is designed
to be compatible with vscodes’ behavior, which can be counterintuitive when
the snippet can be expanded at places other than the point where selection
started (or when doing transformations on selected text).
All `TM_something`-variables are supported with two additions: `LS_SELECT_RAW`
and `LS_SELECT_DEDENT`. These were introduced because `TM_SELECTED_TEXT` is
designed to be compatible with vscodes’ behavior, which can be
counterintuitive when the snippet can be expanded at places other than the
point where selection started (or when doing transformations on selected text).
Besides those we also provide `LS_TRIGGER` which contains the trigger of the
snippet, and `LS_CAPTURE_n` (where n is a positive integer) that contains the
n-th capture when using a regex with capture groups as `trig` in the snippet
definition.

All variables can be used outside of lsp-parsed snippets as their values are
stored in a snippets’ `snip.env`-table:
Expand All @@ -1353,7 +1357,7 @@ stored in a snippets’ `snip.env`-table:
s("selected_text", f(function(args, snip)
local res, env = {}, snip.env
table.insert(res, "Selected Text (current line is " .. env.TM_LINE_NUMBER .. "):")
for _, ele in ipairs(env.SELECT_RAW) do table.insert(res, ele) end
for _, ele in ipairs(env.LS_SELECT_RAW) do table.insert(res, ele) end
return res
end, {}))
<
Expand All @@ -1376,9 +1380,11 @@ where:
Is a function that receives a string and returns a value for the var with that name
or a table from var name to a value
(in this case, if the value is a function it will be executed lazily once per snippet expansion).
- `init`: `fn(pos: pair[int])->map[string, EnvVal]` Takes the 0-based position of the cursor and returns
- `init`: `fn(info: table)->map[string, EnvVal]` Returns
a table of variables that will set to the environment of the snippet on expansion,
use this for vars that have to be calculated in that moment or that depend on each other.
The `info` table argument contains `pos` (0-based position of the cursor on expansion),
the `trigger` of the snippet and the `captures` list.
- `eager`: `list[string]` names of variables that will be taken from `vars` and appended eagerly (like those in init)
- `multiline_vars`: `(fn(name:string)->bool)|map[sting, bool]|bool|string[]` Says if certain vars are a table or just a string,
can be a function that get’s the name of the var and returns true if the var is a key,
Expand All @@ -1405,7 +1411,7 @@ A simple example to make it more clear:
-- then you can use $SYS_HOME which was eagerly initialized but also $SYS_USER (or any other system environment var) in your snippets
lsp.env_namespace("POS", {init=function(pos) return {"VAL": vim.inspect(pos)}} end)
lsp.env_namespace("POS", {init=function(info) return {"VAL": vim.inspect(info.pos)}} end)
-- then you can use $POS_VAL in your snippets
Expand Down
4 changes: 3 additions & 1 deletion lua/luasnip/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ local function snip_expand(snippet, opts)
snip.trigger = opts.expand_params.trigger or snip.trigger
snip.captures = opts.expand_params.captures or {}

local env = Environ:new(opts.pos)
local info =
{ trigger = snip.trigger, captures = snip.captures, pos = opts.pos }
local env = Environ:new(info)

local pos_id = vim.api.nvim_buf_set_extmark(
0,
Expand Down
14 changes: 11 additions & 3 deletions lua/luasnip/util/_builtin_vars.lua
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,27 @@ function lazy_vars.BLOCK_COMMENT_END()
end

-- These are the vars that have to be populated once the snippet starts to avoid any issue
local function eager_vars(pos)
local function eager_vars(info)
local vars = {}
local pos = info.pos
vars.TM_CURRENT_LINE =
vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false)[1]
vars.TM_CURRENT_WORD = util.word_under_cursor(pos, vars.TM_CURRENT_LINE)
vars.TM_LINE_INDEX = tostring(pos[1])
vars.TM_LINE_NUMBER = tostring(pos[1] + 1)
vars.SELECT_RAW, vars.SELECT_DEDENT, vars.TM_SELECTED_TEXT =
vars.LS_SELECT_RAW, vars.LS_SELECT_DEDENT, vars.TM_SELECTED_TEXT =
util.get_selection()
-- These are for backward compatibility, for now on all builtins that are not part of TM_ go in LS_
vars.SELECT_RAW, vars.SELECT_DEDENT =
vars.LS_SELECT_RAW, vars.LS_SELECT_DEDENT
for i, cap in ipairs(info.captures) do
vars["LS_CAPTURE_" .. i] = cap
end
vars.LS_TRIGGER = info.trigger
return vars
end

local builtin_ns = { SELECT = true }
local builtin_ns = { SELECT = true, LS = true }

for name, _ in pairs(lazy_vars) do
local parts = vim.split(name, "_")
Expand Down
5 changes: 3 additions & 2 deletions lua/luasnip/util/environ.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ function Environ.is_table(var_fullname)
return nmsp.is_table(varname)
end

function Environ:new(pos, o)
function Environ:new(info, o)
o = o or {}
setmetatable(o, self)
vim.list_extend(info, info.pos) -- For compatibility with old user defined namespaces

for ns_name, ns in pairs(namespaces) do
local eager_vars = {}
if ns.init then
eager_vars = ns.init(pos)
eager_vars = ns.init(info)
end
for _, eager in ipairs(ns.eager) do
if not eager_vars[eager] then
Expand Down
8 changes: 4 additions & 4 deletions tests/integration/parser_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ describe("Parser", function()

it("Inserts default when the variable is empty", function()
ls_helpers.session_setup_luasnip()
local snip = "${SELECT_DEDENT: a ${2:default}}"
local snip = "${LS_SELECT_DEDENT: a ${2:default}}"

exec_lua("ls.lsp_expand([[" .. snip .. "]])")

Expand Down Expand Up @@ -678,7 +678,7 @@ describe("Parser", function()

it("handles default correctly inside placeholder", function()
ls_helpers.session_setup_luasnip()
local snip = "${1: ${SELECT_DEDENT: a ${2:default}} }"
local snip = "${1: ${LS_SELECT_DEDENT: a ${2:default}} }"

exec_lua("ls.lsp_expand([[" .. snip .. "]])")

Expand Down Expand Up @@ -708,7 +708,7 @@ describe("Parser", function()

it("handles copy-source inside default.", function()
ls_helpers.session_setup_luasnip()
local snip = "${1: ${SELECT_DEDENT: a ${2:default} ${3:copied}}} $3"
local snip = "${1: ${LS_SELECT_DEDENT: a ${2:default} ${3:copied}}} $3"

exec_lua("ls.lsp_expand([[" .. snip .. "]])")

Expand Down Expand Up @@ -745,7 +745,7 @@ describe("Parser", function()

it("handles copy inside default", function()
ls_helpers.session_setup_luasnip()
local snip = "$1 ${2: ${SELECT_DEDENT: a ${3:default} $1} }"
local snip = "$1 ${2: ${LS_SELECT_DEDENT: a ${3:default} $1} }"

-- indent, insert text, SELECT.
exec_lua("ls.lsp_expand([[" .. snip .. "]])")
Expand Down
39 changes: 34 additions & 5 deletions tests/unit/environ_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe("luasnip.util.environ", function()
local Environ = require("luasnip.util.environ")
%s
local env = Environ:new({0, 0})
local env = Environ:new({pos={0, 0}, captures={}, trigger=""})
local result = env["%s"]
return #(result) > 0
]=]):format(
Expand All @@ -30,7 +30,7 @@ describe("luasnip.util.environ", function()
local Environ = require("luasnip.util.environ")
%s
local env = Environ:new({0, 0})
local env = Environ:new({pos={0, 0}, captures={}, trigger=""})
return env["%s"]
]=]):format(
namespace_setup,
Expand All @@ -48,7 +48,7 @@ describe("luasnip.util.environ", function()
([=[
local Environ = require("luasnip.util.environ")
%s
local env = Environ:new({0, 0})
local env = Environ:new({pos={0, 0}, captures={}, trigger=""})
return env["%s"] == nil
]=]):format(
namespace_setup,
Expand All @@ -71,7 +71,7 @@ describe("luasnip.util.environ", function()
([=[
local Environ = require("luasnip.util.environ")
%s
local env = Environ:new({0, 0})
local env = Environ:new({pos={0, 0}, captures={}, trigger=""})
return rawget(env, "%s") ~= nil
]=]):format(
namespace_setup,
Expand Down Expand Up @@ -144,9 +144,16 @@ describe("luasnip.util.environ", function()
false,
"VAR"
)
check(
"Init funtion old api",
[[Environ.env_namespace("OLD", {init=function(pos) return {POS = table.concat(pos, ',')} end})]],
"OLD_POS",
true,
"0,0"
)
check(
"Init funtion",
[[Environ.env_namespace("IN", {init=function(pos) return {POS = table.concat(pos, ',')} end})]],
[[Environ.env_namespace("IN", {init=function(info) return {POS = table.concat(info.pos, ',')} end})]],
"IN_POS",
true,
"0,0"
Expand Down Expand Up @@ -180,4 +187,26 @@ describe("luasnip.util.environ", function()
"Environ with multiline_vars incorrect type",
[[Environ.env_namespace("TES_T", {var={A='s', multiline_vars = 9 }})]]
)

local function check_builtin(var_name, test)
it("Test builtin " .. var_name, function()
assert.is_true(
exec_lua(
([=[
local Environ = require("luasnip.util.environ")
local env = Environ:new({pos={0, 0}, captures={"one"}, trigger="trigg"})
local result = env["%s"]
local test = %s
return test(result)
]=]):format(
var_name,
test
)
)
)
end)
end

check_builtin("LS_TRIGGER", [[function(r) return r == "trigg" end]])
check_builtin("LS_CAPTURE_1", [[function(r) return r == "one" end]])
end)

0 comments on commit c919d04

Please sign in to comment.