nvim-surround icon indicating copy to clipboard operation
nvim-surround copied to clipboard

Multi-character string delimiters

Open ObserverOfTime opened this issue 1 year ago • 2 comments

Checklist

  • [x] Have you read through :h nvim-surround to see if there might be any relevant information there?

Is your feature request related to a problem? Please describe.

Several languages support strings that use multiple characters as delimiters.

  • C#: """string"""
  • Kotlin: """string"""
  • Python: """string""" or '''string'''
  • Groovy: """string""" or '''string''' or $/string/$
  • Lua: [[string]] or [=[string]=] (any number of =)
  • Rust: ##string## (any number of #)
  • Java ``string`` (any number of `)

Describe the solution you'd like

It'd be nice if we could change single-character delimited strings to multi-character and vice versa.

Additional context

Some languages also support prefixes but that would probably be too complicated.

  • C#: @"string" or $"string" (any supported delimiter, can be mixed, any number of $)
  • Python: b"string" or r"string" or f"string" (any supported delimiter, can be mixed)
  • Rust: b"string" or r"string" (any supported delimiter, can be mixed)
  • Bash: $'string' (any supported delimiter)
  • JavaScript: String.raw`string` (prefix can be any function)

ObserverOfTime avatar Aug 29 '22 07:08 ObserverOfTime

To my knowledge (if I'm understanding correctly) this should already be possible in the plugin. It's certainly not the most elegant, but here's a code sample for Lua strings:

require("nvim-surround").setup({
    surrounds = {
        ["s"] = {
            add = function()
                local count = require("nvim-surround.config").get_input("Enter a count: ")
                return { { "[" .. ("="):rep(count) .. "[" }, { "]" .. ("="):rep(count) .. "]" } }
            end,
            find = "%[(=*)%[.-%]%1%]",
            delete = "^(%[=*%[)().-(%]=*%])()$",
        },
    },
    aliases = {
        ["s"] = false,
    },
})

The other ones that you mentioned should also be possible, through a mix of using config.get_input() and good Lua pattern-matching.

kylechui avatar Aug 29 '22 22:08 kylechui

I guess this could also be implemented using counts like #147. It would have to support two counts, one for selecting and one for replacing.

For example:

  • cs"2] would change "string" to [[string]].
  • 2cs#" would change ##string## to "string"
  • 3cs'3" would change '''string''' to """string""".

(The first count could come either before or after cs, whichever you think makes more sense.)


Workarounds for $/string/$ and [=[string]=] which can't be implemented that way:

 -- add this to init.lua because it's not built-in
vim.keymap.set({'o', 'x'}, 'a/', ':<C-U>normal F/vf/<CR>', {silent = true})
  • cs'/ysa/$ would change 'string' to $/string/$
  • cs'2]ysa]= would change 'string' to [=[string]=]
  • cs"2]ysa]2= would change "string" to [==[string]==]

ObserverOfTime avatar Aug 30 '22 08:08 ObserverOfTime

Going to close this issue in favor of #147, which seems to be a good implementation strategy for this idea.

kylechui avatar Mar 15 '23 19:03 kylechui