Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOC mistakes #510

Closed
zjp-CN opened this issue Aug 8, 2022 · 10 comments
Closed

DOC mistakes #510

zjp-CN opened this issue Aug 8, 2022 · 10 comments

Comments

@zjp-CN
Copy link
Contributor

zjp-CN commented Aug 8, 2022

nonempty(n, "empty!", "not empty!") inserts "empty!" if insert n is empty, "not empty!" if it isn't

nonempty(n, "non empty!", "empty!") makes sense in fact

nonempty = function(indx, text_if, text_if_not)
assert(
type(indx) == "number",
"this only checks one node for emptiness!"
)
assert(
text_if,
"At least the text for nonemptiness has to be supplied."
)
return F(function(args)
return (args[1][1] ~= "" or #args[1] > 1) and text_if
or (text_if_not or "")
end, {
indx,
})
end,

@zjp-CN
Copy link
Contributor Author

zjp-CN commented Aug 8, 2022

Some other mistakes in the documentation are found too. I can open a PR to fix them.

@L3MON4D3
Copy link
Owner

L3MON4D3 commented Aug 8, 2022

Ohh, yes you're right, easy to get confused with wording there :P
Please do, I very much appreciate improvements to the documentation :)

@zjp-CN
Copy link
Contributor Author

zjp-CN commented Aug 9, 2022

The sentence in here is hard to understand:

multiline_vars: (fn(name:string)->bool)|map[sting, bool]|bool 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, a list of vars that are tables or a boolean for the full namespace, it's false by default.

Why we need that argument? It would be better to add an example for it.
I found a snippet in the test suits:

exec_lua([[
ls.env_namespace("DYN", {
vars = {ONE = "1", TWO = {"1", "2"}},
multiline_vars = {"TWO"}
})
]])
local snip = [[
s("trig", {
d(1, function(args, parent)
return sn(nil, {
t(parent.snippet.env.DYN_ONE),
t"..",
t(parent.snippet.env.DYN_TWO),
t"..",
t(tostring(#parent.snippet.env.DYN_TWO)), -- This one behaves as a table
t"..",
t(parent.snippet.env.WTF_YEA), -- Unknow vars also work
})
end, {})
})
]]

The expansion is:

1..1
2..2..

which I still don't understand, because

  • a list of string is passed in multiline_vars, which doesn't belong to (fn(name:string)->bool)|map[sting, bool]|bool
  • if the line of multiline_vars is commented, the expansion is the same

@zjp-CN zjp-CN changed the title DOC mistake: nonempty parameters' meaning DOC mistakes Aug 9, 2022
@L3MON4D3
Copy link
Owner

L3MON4D3 commented Aug 9, 2022

Why we need that argument?

That's a bit involved 😅:
With regular expansion multiline_vars does not make a difference, it becomes necessary once get_docstring() is called on some snippet. This function tries to expand the snippet without actually having a buffer to work with, and returns an accurate string-representation of the snippet.
This expansion also includes evaluating dynamicNodes, so their functions have to still work when called in this static (that's how it's called in most of the codebase, basically without a correct associated buffer) context.
So, we make some effort to have as many functions as possible still work as-is.
One of these efforts is replacing the value of variables (because stuff like TM_FILENAME cannot be filled accurately when there is no buffer) with their name (parent.snippet.env.TM_FILENAME -> "$TM_FILENAME") (see fake_env) (This has the added benefit of leading to more descriptive docstrings)
Now, there's a problem: We want to avoid errors when evaluating functions, but functions can contain both code like parent.snippet.env.STRING_VAR .. "some string" and parent.snippet.env.MULTILINE_VAR[1], but both should still evaluate successfully when get_docstring()d.
So, we have to return the expected type from fake_env, but we don't know which type a variable has (string vs string[]), until we evaluate the variable, which we probably don't want to do in that "static" context. Because of that we require that namespaces announce which of their variables are tables, and which are strings (this is implicit ofc.), so we can return the correct type from fake_env.

You can see the difference by calling print(snip.get_docstring()[1]) on that snippet (here both run successfully, just the # returns a different number)

I agree that some motivation for this option should be given, a link to this comment might do it.

a list of string is passed in multiline_vars, which doesn't belong to (fn(name:string)->bool)|map[sting, bool]|bool

Ouh, yeah, string[] should be appended to it 😅

@zjp-CN
Copy link
Contributor Author

zjp-CN commented Aug 12, 2022

It's much clear for me after some local testing:

No multiline_vars

ls.env_namespace("DYN", {
  vars = { ONE = "1", TWO = { "1", "2" } },
})

And given the same function node:

ls.add_snippets("all", {
  s("dyn_addsnip", d(1, function(args, parent)
    return sn(nil, {
      t(parent.snippet.env.DYN_ONE),
      t "..",
      t(table.concat(parent.snippet.env.DYN_TWO)),
      t "..",
      t(tostring(#parent.snippet.env.DYN_TWO)), -- This one behaves as a table
      t "..",
      t(parent.snippet.env.WTF_YEA), -- Unknow vars also work
    })
  end, {}))
})
local dyn = s("dyn_return", d(1, function(args, parent)
  return sn(nil, {
    t(parent.snippet.env.DYN_ONE),
    t "..",
    t(table.concat(parent.snippet.env.DYN_TWO)),
    t "..",
    t(tostring(#parent.snippet.env.DYN_TWO)), -- This one behaves as a table
    t "..",
    t(parent.snippet.env.WTF_YEA), -- Unknow vars also work
  })
end, {}))

return { dyn }

Run both of thme and we get the same result 1..12..2.., which works perfectly.

multiline_vars is a must

However if we use get_docstring without multiline_vars, like this

return {
  parse("dyn_parse", table.concat(dyn:get_docstring())),
}

An error pops up:

Error while evaluating dynamicNode@1 for snippet 'dyn_return':
[string "require("luasnip").setup_snip_env() -- local ..."]:31: bad argument #1 to 'concat' (table expected, got string)

So the solution is:

ls.env_namespace("DYN", {
  vars = { ONE = "1", TWO = { "1", "2" } },
  multiline_vars = { "TWO" }
})

But there are some weird things in the case:

  • the result of print(dyn:get_docstring()[1]) is ${1:$DYN_ONE..$DYN_TWO..1..$WTF_YEA}$0: the number 1 should be 2
  • the expansion of dyn_parse is
    1..1
    2..1..
    
    which differs with 1..12..2.. and is not correct now.

@L3MON4D3
Copy link
Owner

But there are some weird things in the case:

  • the result of print(dyn:get_docstring()[1]) is ${1:$DYN_ONE..$DYN_TWO..1..$WTF_YEA}$0: the number 1 should be 2

Mhmm, I can see how that would be better in this case, but in general, I'm not convinced that matching the number of lines in an env-var really provides additional value.
I don't think there are any snippets that really depend on some env-variable-table containing ie. exactly two entries.

  • the expansion of dyn_parse is
    1..1
    2..1..
    

Oh, mhmmm, that's unexpected, no idea where that linebreak is coming from o.O

@L3MON4D3
Copy link
Owner

Ahh nvm, of course it's coming from DYN_TWO, it contains a linebreak after all.
So ofc, parsing ${1:$DYN_ONE..$DYN_TWO..1..$WTF_YEA}$0 yields a new snippet with those variables (check out the parse_from_ast-branch, there the WTF_YEA also show up correctly)

@L3MON4D3
Copy link
Owner

(Btw, funny idea feeding the generated docstrings back to luasnip, hadn't occured to me as of yet :D)

@zjp-CN
Copy link
Contributor Author

zjp-CN commented Aug 12, 2022

So, what's the canonical way to use snip:get_docstring()? :)

@L3MON4D3
Copy link
Owner

You can see one usage here, so it's mostly used to show what a snippet looks like in completion-engines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants