kylechui / nvim-surround

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

Add emmet support for html tag expansion #186

Closed otijhuis closed 1 year ago

otijhuis commented 1 year ago

Checklist

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

Describe the solution you'd like Emmet support by default so you can use .some-class#test as tag for instance and have it expand to <div id="test" class="some-class"></div>. vim-sandwich allows this too.

Additional context

It's already possible by overriding the current configuration, although it only worked on master, not with the current tag. Here's my current packer configuration. It is uses the emmet-vim plugin.

  use {
    'kylechui/nvim-surround',
    disable = false,
    -- tag = "*", -- Use for stability; omit to use `main` branch for the latest features
    config = function()
      local get_expanded_surrounds = function(input)
        -- expand with emmet, use 1 as last param to keep the "${cursor}" strings
        local expansion = vim.fn["emmet#expandWord"](input, "html", 1)
        -- use last "${cursor}" position to place the content to be wrapped, there can be multiple "${cursor}" strings
        local open_end_pos, close_start_pos = string.match(expansion, ".*()${cursor}()")

        -- remove all extra "${cursor}" strings
        local open_text = string.gsub(string.sub(expansion, 1, open_end_pos - 1), "${cursor}", "")
        local close_text = vim.fn.trim(string.sub(expansion, close_start_pos))

        -- split by newline
        local open = vim.split(open_text, "\n")
        local close = vim.split(close_text, "\n")

        return open, close
      end

      require('nvim-surround').setup({
        surrounds = {
          ["t"] = {
              add = function()
                local input = require('nvim-surround.config').get_input("Enter the HTML tag: ")
                if input then
                  local open, close = get_expanded_surrounds(input)

                  return { open, close }
                end
              end,
              find = function()
                return require('nvim-surround.config').get_selection({ motion = "at" })
              end,
              delete = "^(%b<>)().-(%b<>)()$",
              change = {
                target = "^<([^%s<>]*)().-([^/]*)()>$",
                replacement = function()
                  local input = require('nvim-surround.config').get_input("Enter the HTML tag: ")
                  if input then
                    local element = input:match("^<?([^%s>]*)")
                    local attributes = input:match("^<?[^%s>]*%s+(.-)>?$")

                    local open = attributes and element .. " " .. attributes or element
                    local close = element

                    return { { open }, { close } }
                  end
                end,
              },
          },
          ["T"] = {
            add = function()
              local input = require('nvim-surround.config').get_input("Enter the HTML tag: ")
              if input then
                local open, close = get_expanded_surrounds(input)

                return { open, close }
              end
            end,
            find = function()
              return require('nvim-surround.config').get_selection({ motion = "at" })
            end,
            delete = "^(%b<>)().-(%b<>)()$",
            change = {
              target = "^(%b<>)().-(%b<>)()$",
              replacement = function()
                local input = require('nvim-surround.config').get_input("Enter the HTML tag: ")
                if input then
                  local open, close = get_expanded_surrounds(input)

                  return { open, close }
                end
              end,
            },
          },
        }
      })
    end,
    requires = { 'mattn/emmet-vim' }
  }
kylechui commented 1 year ago

For the purposes of keeping this plugin simple/lightweight, I feel that it doesn't make sense for me to implement any language-specific features by default. For example, LaTeX and Markdown support is not baked in, but rather extended via configurations (see #53).