kylechui / nvim-surround

Add/change/delete surrounding delimiter pairs with ease. Written with :heart: in Lua.
MIT License
3.18k stars 62 forks source link

Custom multi-line surround action doesn't work #88

Closed mawkler closed 2 years ago

mawkler commented 2 years ago

Checklist

Describe the bug

I'm trying to create a custom surround action for surrounding text in a Markdown code-block, but I can't get it to create new lines.

To Reproduce

require('nvim-surround').setup({
  delimiters = {
    separators = {
      c = { {'```'}, {'```'} }
    }
  }
})

Doing yssc on the following line:

lorem ipsum dolor sit amet, con

yields this

```lorem ipsum dolor sit amet```

Expected behavior

lorem ipsum dolor sit amet

I've also tried putting my config in pairs instead of separators.

kylechui commented 2 years ago

This is intended behavior; as yss adds the surrounding pair to the left and right of the current line. Either use VS for now, or wait until I add support for the ySS command.

kylechui commented 2 years ago

See #58.

mawkler commented 2 years ago

I see! The documentation implies that multi-line strings are already supported:

Note: Multi-line strings are represented by a table of strings, with each string representing a new line.

Perhaps it should be removed from the docs until it has been added? :)

kylechui commented 2 years ago

I see! The documentation implies that multi-line strings are already supported:

Note: Multi-line strings are represented by a table of strings, with each string representing a new line.

Perhaps it should be removed from the docs until it has been added? :)

@melkster Perhaps it's a bit unclear, but that line refers to the idea that a surround: { "foo", "bar", "baz" } refers to the text

foo
bar
baz

However, if we surround a word text with such a delimiter pair, then the buffer becomes

foo
bar
baztext -- Right hand side delimiter

There is no new line after the final string (by default), and the idea is that one will be added whenever I get around to implementing ySS in #58. P.S. If you don't mind, please read through the final messages and let me know which naming scheme you would prefer for the new commands, since I think adding new ones will require some sort of breaking change

mawkler commented 2 years ago

I see! I noticed that #58 has been merged, does that mean that I can create such a surround object that's always line-wise? If yes, how would I create one?

kylechui commented 2 years ago

@melkster The idea is that any mapping that uses _line suffix will always try to put the delimiters on new lines, so using your current configuration, ySSc should work, or highlight the selection in visual line mode using V, then Sc works.

mawkler commented 2 years ago

I understand, however with use cases like surrounding markdown text in a code block you always want it to be line-wise, and I think it would be useful for other use cases as well to be able to create a surround object that is always line-wise and never character-wise. For example, I would always want to transform this:

not code, code, not code

to this:

not code,

code

, not code

and I would never want it to be transformed into this:

not code, ```code```, not code

Therefore I think it's reasonable to be able to get ysiwc to achieve that as well.

kylechui commented 2 years ago

I understand, however with use cases like surrounding markdown text in a code block you always want it to be line-wise, and I think it would be useful for other use cases as well to be able to create a surround object that is always line-wise and never character-wise. For example, I would always want to transform this:

not code, code, not code

to this:

not code,

code

, not code

and I would never want it to be transformed into this:

not code, ```code```, not code

Therefore I think it's reasonable to be able to get ysiwc to achieve that as well.

@melkster Ah, sorry for misunderstanding; what you're asking for has always been possible via multi-line surrounds:

require('nvim-surround').setup({
    delimiters = {
        pairs = {
            c = { { "", "```", "" }, { "", "```", "" } },
        },
    },
}

The idea is that instead of having a string for each delimiter, you can use a table of strings, where each element of the table indicates a new line. The configuration noted above is effectively { "\n```\n", "\n```\n" }, inserting the new lines for you. Please let me know if you still have any questions!

mawkler commented 2 years ago

Thank you that works for me! Perhaps this could be clarified slightly in the helpdocs :)

The only comment I have now is that the indentation becomes kinda funny when you surround an indented text-object. For example, doing yssc (with your solution for the c pair) on this text:

  beep boop

becomes this:

beep boop

Perhaps the indentation should be preserved for each line?

kylechui commented 2 years ago

Perhaps this could be clarified slightly in the helpdocs :)

Unfortunately I've been bogged down by #101; I'll add that to the list of things to clarify in the wiki though.

Also unfortunately, I won't be "fixing" indentation for normal-mode surrounds, at least not until this issue is integrated upstream. My original plan was to use the = operator to format lines, which would integrate well with Tree-sitter or whatever indentation program to try and produce the most consistent results; but using that operator would destroy dot-repeatability. As of the latest commit on main, formatting issues are handled via = for delete/change actions (they are internally a bit different from normal mode surrounds), and insert-mode surrounds are handled separately as well.

At this point I'm much more concerned with trying to get custom deletions into the project instead of these "whitespace" issues (see #37, #76), and I would just recommend that you get a code formatter. The main idea to grasp here is that there are a myriad of ways to answer "how should the whitespace look after an action", e.g. for your case an equally valid answer would be for everything to be dedented and flush with the left, or even the middle line indented more than the code fence itself. Hopefully it makes sense why I won't be putting in too much more time from a development standpoint, and don't hesitate to ask any more questions :)

mawkler commented 2 years ago

Thank you for your answer, and I completely understand! I'm very impressed by your dedication at making such a great surround plugin. Thank you for your hard work!

kylechui commented 2 years ago

Thanks for taking the time to open issues like this :) It really helps me make this better, bit by bit. If you want to help out even more, you could take a look through the discussions pages (especially the ones marked [Design]) and weigh in on a few user interface decisions that I'm trying to make

mawkler commented 2 years ago

Sure! :)

gibfahn commented 9 months ago

Thanks for this issue, I was trying to implement something similar and was confused by the error message (and the help text mentioned above):

E5108: Error executing lua ...are/nvim/lazy/nvim-surround/lua/nvim-surround/buffer.lua:233: 'replacement string' item contains newlines
stack traceback:
    [C]: in function 'nvim_buf_set_text'
    ...are/nvim/lazy/nvim-surround/lua/nvim-surround/buffer.lua:233: in function 'insert_text'
    ...share/nvim/lazy/nvim-surround/lua/nvim-surround/init.lua:68: in function 'normal_surround'
    ...share/nvim/lazy/nvim-surround/lua/nvim-surround/init.lua:297: in function <...share/nvim/lazy/nvim-surround/lua/nvim-surround/init.lua:246>

This issue was helpful, although I think the config looks a bit different now. I added a version of this to https://github.com/kylechui/nvim-surround/discussions/53#discussioncomment-8028781