gaoDean / autolist.nvim

Automatic list continuation and formatting for neovim, powered by lua
MIT License
242 stars 5 forks source link

<CR> not working with autopairs plugin #43

Closed goingtosleep closed 1 year ago

goingtosleep commented 1 year ago

When I set create_mapping_hook("i", "<cr>", require("autolist").new), it will kind of override the of autopairs plugins (like auto-pairs) and results in the following error:

Error detected while processing BufEnter Autocommands for "*"..function autopairs#AutoPai
rsTryInit[14]..autopairs#AutoPairsInit[17]..autopairs#Keybinds#mapKeys:                  
line   27:                                                                               
E716: Key not present in Dictionary: "rhs"                                               
line   28:                                                                               
E121: Undefined variable: old_cr                                                         
E116: Invalid arguments for function autopairs#Keybinds#ExpandMap                        
line   29:                                                                               
E121: Undefined variable: old_cr                                                         
E116: Invalid arguments for function substitute                                          
line   52:                                                                               
E121: Undefined variable: old_cr                                                         
Press ENTER or type command to continue

It was still working fine with commit 761e0f9, but that commit has the annoying space in the beginning of the new line.

My setup:

Reproduce

Run nvim -u repro.lua tmp.md. The file repro.lua is given below.

repro.lua

local root = vim.fn.fnamemodify("./.repro", ":p")

-- set stdpaths to use .repro
for _, name in ipairs({ "config", "data", "state", "cache" }) do
  vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end

-- bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "--single-branch",
    "https://github.com/folke/lazy.nvim.git",
    lazypath,
  })
end
vim.opt.runtimepath:prepend(lazypath)

-- install plugins
local plugins = {
  "folke/tokyonight.nvim",
  "LunarWatcher/auto-pairs",
  {
    "gaoDean/autolist.nvim",
    config = function()
      local autolist = require("autolist")
      autolist.setup()

      local function create_mapping_hook(mode, mapping, hook, alias)
        vim.keymap.set(
        mode,
        mapping,
        function(motion)
          local keys = hook(motion, alias or mapping)
          if not keys then keys = '' end
          return keys
        end,
        { expr = true }
        )
      end

      create_mapping_hook("i", "<CR>", autolist.new)
      create_mapping_hook("n", "o", autolist.new)
    end,
  },
}

require("lazy").setup(plugins, {
  root = root .. "/plugins",
})

-- add anything else here
vim.opt.termguicolors = true
-- do not remove the colorscheme!
vim.cmd([[colorscheme tokyonight]])
gaoDean commented 1 year ago

Yeah good pickup, I’ll have to install an auto pairs plugin tmr to see what the issue is.

mawkler commented 1 year ago

I noticed a similar issue with autopairs. I didn't get an error but I noticed that autopairs overrides autolist's o mapping. I solved this by adding buffer = true to vim.keymap.set() inside create_mapping_hook() to make the mapping buffer-only, like so:

local function create_mapping_hook(mode, mapping, hook, alias)
  vim.keymap.set(
    mode,
    mapping,
    function(motion)
      local keys = hook(motion, alias or mapping)
      if not keys then keys = '' end
      return keys
    end,
    { expr = true, buffer = true }
  )
end

Perhaps this should be changed in the README since this is the desired behaviour.

goingtosleep commented 1 year ago

@mawkler : I tried adding buffer = true, the error does go away, however the feature of auto-pairs doesn't work. See this gif:

https://user-images.githubusercontent.com/47395035/210237829-0a56c223-37aa-4e95-93f5-33892302b17e.mp4

The expected behavior is like this:

Peek 2023-01-02 20-18

I'm not sure if this is possible as mkdnflow.nvim explicitly mentioned that one has to disable mapping of autopairs. But it worked at commit 761e0f9, so hopefully @gaoDean can do something 😊

gaoDean commented 1 year ago

@goingtosleep I tried using windwp's autopairs but with your example, it does the expected behavior even with and without the autopairs plugin. I tried disabling treesitter and autolist, but the expected behavior still arises. Is there any more complex autopairs-specific behavior that I can try?

gaoDean commented 1 year ago

Okay I tried ((<cr>)) with windwp/nvim-autopairs and the result was

((

))

whereas without autopairs, the result was

((

)

Even if I turned on autolist, nothing changed

gaoDean commented 1 year ago

Here's my config with lazy.nvim and default lazy.nvim settings:

  {
    "windwp/nvim-autopairs",
    config = true,
    enabled = true,
  },
  {
    "gaoDean/autolist.nvim",
    dev = true,
    enabled = true, -- doesn't matter if this is false
    ft = {
      "markdown",
      "txt",
    },
    config = function()
      require("autolist").setup()
      function create_mapping_hook(mode, mapping, hook, alias)
        vim.keymap.set(
          mode,
          mapping,
          function(motion)
            local keys = hook(motion, alias or mapping)
            if not keys then keys = "" end
            return keys
          end,
          { expr = true}
        )
      end

      create_mapping_hook("i", "<cr>", require("autolist").new)
      create_mapping_hook("i", "<tab>", require("autolist").indent)
      create_mapping_hook("i", "<s-tab>", require("autolist").indent, "<c-d>")
      create_mapping_hook("n", "dd", require("autolist").force_recalculate)
      create_mapping_hook("n", "o", require("autolist").new)
      create_mapping_hook("n", "O", require("autolist").new_before)
      create_mapping_hook("n", ">>", require("autolist").indent)
      create_mapping_hook("n", "<<", require("autolist").indent)
      create_mapping_hook("n", "<c-r>", require("autolist").force_recalculate)
      create_mapping_hook("n", "<leader>x", require("autolist").invert_entry, "")
    end,
  }
gaoDean commented 1 year ago

oh I see, I wasn't using the repro.lua.

gaoDean commented 1 year ago

I noticed a similar issue with autopairs. I didn't get an error but I noticed that autopairs overrides autolist's o mapping. I solved this by adding buffer = true to vim.keymap.set() inside create_mapping_hook() to make the mapping buffer-only, like so:

@mawkler For me, o worked with buffer = false too

gaoDean commented 1 year ago

@mawkler @goingtosleep Okay so temporary fix, just set autolist's priority to be very high

gaoDean commented 1 year ago

Alright, more permanent fix, add branch = "dev3", to autolist in the lazy plugins (ik, such a descriptive branch name)

gaoDean commented 1 year ago

whoops didnt mean to close it, but try it out now

goingtosleep commented 1 year ago

Just tried with the latest commit @gaoDean, but it seems the issue is still there 🥲.

Sometimes autopairs works and autolist doesn't: https://user-images.githubusercontent.com/47395035/210298513-64ebaa4e-f0ac-4ccd-b8ca-a880e5c9830c.mp4

Sometimes the reversed: https://user-images.githubusercontent.com/47395035/210298522-ae3ff50f-8fb9-439e-a46b-e833f19f920b.mp4

I tried using the repro.lua with windwp/nvim-autopairs. Is the behavior the same on your side? Do you enable defaults = { lazy = true } in lazy.nvim and specified an order for the plugins?

gaoDean commented 1 year ago

@goingtosleep ohh, I see the problem. They are probably competing for the "" mapping, and when autolist wins, it still presses "<cr>", but with noremap (because of recursion). Then when autopairs wins, the opposite happens. (at least i think that is what is happening)

gaoDean commented 1 year ago

even with this:

            vim.keymap.set("n", "<cr>", function()
                print("test1")
                return "<cr>"
            end, { expr = true, remap = true })
            vim.keymap.set("n", "<cr>", function()
                print("test2")
                return "<cr>"
            end, { expr = true, remap = true })

only the second one runs, so I can't really see a way to do this.

gaoDean commented 1 year ago

The only way I can see is to get the function that is mapped to "" (nvim_buf_get_keymap) in create_mapping_hook, and then call that function, and then do the autolist funtcion.

gaoDean commented 1 year ago

@goingtosleep ok im really close with this now but my dad says i gotta go to bed lmao

(this is all in the config function

map.lhs is the mapping (like <CR>) map.rhs is either a "v:lua." string or a function depending on if it was made with nvim_buf_set_keymap or vim.keymap.set respectively

you must make sure that autopairs is loaded first

nvim-autopairs is annoying cus it uses nvim_buf_set... so theres no callback function, theres only a "v:lua" string

you can see it nearly working if you replace the ending return hook... with return ""


      local autolist = require("autolist")
      autolist.setup()

      local function mapping_hook(mode, mapping, hook, alias)
                -- if the requested mapping is occupied, add current function to it

                local additional_map = nil
                local maps = vim.api.nvim_get_keymap(mode)
                for _, map in ipairs(maps) do
                    if map.lhs == mapping then
                        if map.rhs then
                            additional_map = map.rhs:gsub("^v:lua%.", "", 1)
                        else
                            additional_map = map.callback
                        end
                        pcall(vim.keymap.del, mode, mapping)
                    end
                end
        vim.keymap.set(
                    mode,
                    mapping,
                    function(motion)
                        if additional_map then
                            if type(additional_map) == "string" then
                                local ok, res = pcall(load("return " .. additional_map))
                                if ok then
                                    vim.fn.feedkeys(res, "nt")
                                end
                            else
                                vim.fn.feedkeys(additional_map(), "nt")
                            end
                        end
                        return hook(motion, alias or mapping) or ""
                    end,
                    { expr = true, remap = true }
        )
      end

      mapping_hook("i", "<CR>", require("autolist").new)
gaoDean commented 1 year ago

@goingtosleep try this

local plugins = {
  "folke/tokyonight.nvim",
  {
    "gaoDean/autolist.nvim",
        ft = {
            "markdown",
            "text"
        },
    config = function()
      local autolist = require("autolist")
      autolist.setup()

      local function mapping_hook(mode, mapping, hook, alias)
                local additional_function = nil
                local maps = vim.api.nvim_get_keymap(mode)
                for _, map in ipairs(maps) do
                    if map.lhs == mapping then
                        if map.rhs then
                            additional_function = map.rhs:gsub("^v:lua%.", "", 1)
                        else
                            additional_function = map.callback
                        end
                        pcall(vim.keymap.del, mode, mapping)
                    end
                end
        vim.keymap.set(
                    mode,
                    mapping,
                    function(motion)
                        local additional_map = nil
                        if additional_function then
                            if type(additional_function) == "string" then
                                local ok, res = pcall(load("return " .. additional_function))
                                if ok then
                                    additional_map = res or ""
                                end
                            else
                                additional_map = additional_function() or ""
                            end
                        end
                        return hook(motion, additional_map or mapping) or ""
                    end,
                    { expr = true, buffer = true }
        )
      end

      mapping_hook("i", "<CR>", autolist.new)
      mapping_hook("n", "o", require("autolist").new)
    end,
        dependencies = {
            {
                "windwp/nvim-autopairs",
                config = true,
                event = "VeryLazy"
            },
        },
  },
}
gaoDean commented 1 year ago

i might add this to autolist so the user wont have to copy in a massive function

gaoDean commented 1 year ago

okay 2nd revision because for the above one, autopairs wont work for filetypes that are not markdown and text

local plugins = {
    "folke/tokyonight.nvim",
    {
        "windwp/nvim-autopairs",
        config = true,
        priority = 2,
    },
    {
        "gaoDean/autolist.nvim",
        priority = 1, -- load after autopairs
        branch = "dev2",
        ft = {
            "markdown",
            "text"
        },
        config = function()
            local autolist = require("autolist")
            autolist.setup()
            autolist.create_mapping_hook("i", "<CR>", autolist.new)
            autolist.create_mapping_hook("n", "o", autolist.new)
        end,
    },
}
goingtosleep commented 1 year ago

Nice @gaoDean, the dev2 branch worked for me. Thanks for the great effort 😊