kevinhwang91 / nvim-ufo

Not UFO in the sky, but an ultra fold in Neovim.
BSD 3-Clause "New" or "Revised" License
2.16k stars 37 forks source link

Custom keymap causes nvim to freeze after executing closeFoldsWith() #184

Closed serranomorante closed 7 months ago

serranomorante commented 7 months ago

Neovim version (nvim -v | head -n1)

nightly

Operating system/version

arch linux

How to reproduce the issue

Sorry for the over complicated setup. I'm almost sure this is not an issue with nvim-ufo but... I still opened the issue just in case.

About this capture: after moving my code 1 line below, I try to press 3zm to close folds on the third level but nvim just freezes and I have to kill the shell.

t-rec_41

This issue only happens after using this a very famous custom keymap that helps me to move lines up and down. Additionally, it only happens when the line I'm moving is a comment, either on tsserver or lua_ls.

vim.keymap.set("v", "J", ":m '>+1<CR>gv=gv")
vim.keymap.set("v", "K", ":m '<-2<CR>gv=gv")

Steps to replicate

Put ufo.lua and test.js in the same folder and go to that folder:

  1. Do nvim --clean +'so ufo.lua'
  2. Wait for a bit until everthing gets setup correctly (even the tsserver lsp)
  3. Do :e test.js and wait for ufo folds to be ready
  4. Go to line :4 (the js comment)
  5. Enter line visual mode (shift+V)
  6. Press (shift+J) to move your selected line below (this uses the aforementioned keymap)
  7. Press escape to clear the visual mode
  8. Press 3zm to close folds on the third level

Your nvim instance will freeze completely and you won't be able to do anything.

ufo.lua

for _, name in ipairs({ "config", "data", "state", "cache" }) do
  vim.env[("XDG_%s_HOME"):format(name:upper())] = "/tmp/nvim-debug/" .. name
end

--------------------------------------------------------------------------------

vim.keymap.set("v", "J", ":m '>+1<CR>gv=gv")
vim.keymap.set("v", "K", ":m '<-2<CR>gv=gv")

--------------------------------------------------------------------------------

local plugins = {
  -- setup mason
  {
    "williamboman/mason.nvim",
    lazy = false,
    cmd = { "Mason" },
    config = true,
  },
  {
    "williamboman/mason-lspconfig.nvim",
    lazy = true,
    config = function(_, opts)
      require("mason-lspconfig").setup(opts)
      local emit_event = function()
        vim.api.nvim_exec_autocmds("User", { pattern = "CustomMasonLspSetup", modeline = false })
      end

      vim.schedule(emit_event)
    end,
  },
  {
    "WhoIsSethDaniel/mason-tool-installer.nvim",
    event = "User CustomMasonLspSetup",
    cmd = { "MasonToolsInstall" },
    opts = {
      ensure_installed = {
        "tsserver",
      },
    },
    config = function(_, opts)
      local mason_tool_installer = require("mason-tool-installer")
      mason_tool_installer.setup(opts)
      -- As this plugin is lazy loaded, the original event (VimEnter) will never get trigger
      -- That's why I'm forcing the `run_on_start()` trigger
      mason_tool_installer.run_on_start()
    end,
  },

  -- Setup lsp
  {
    "neovim/nvim-lspconfig",
    event = "BufEnter",
    dependencies = {
      "williamboman/mason-lspconfig.nvim",
      "hrsh7th/nvim-cmp",
      "hrsh7th/cmp-nvim-lsp",
    },
    config = function()
      local lspconfig = require("lspconfig")
      local cmp_nvim_lsp = require("cmp_nvim_lsp")

      local servers = require("mason-lspconfig").get_installed_servers()

      local capabilities =
        vim.tbl_deep_extend("force", lspconfig.util.default_config, cmp_nvim_lsp.default_capabilities())

      capabilities.textDocument.foldingRange = {
        dynamicRegistration = false,
        lineFoldingOnly = true,
      }

      for _, server in pairs(servers) do
        lspconfig[server].setup({
          capabilities = capabilities,
        })
      end

      vim.api.nvim_create_autocmd("User", {
        desc = "set up LSP servers after mason-lspconfig",
        pattern = "CustomMasonLspSetup",
        once = true,
        callback = function() vim.api.nvim_exec_autocmds("FileType", {}) end,
      })
    end,
  },

  -- Ufo
  {
    "kevinhwang91/nvim-ufo",
    dependencies = "kevinhwang91/promise-async",
    lazy = false,
    keys = {
      {
        "zm",
        function() require("ufo").closeFoldsWith() end,
        desc = "Close folds with level",
      },
    },
    init = function()
      vim.opt.foldcolumn = "1"
      vim.opt.foldlevel = 99 -- set high foldlevel for nvim-ufo
      vim.opt.foldlevelstart = 99 -- start with all code unfolded
      vim.opt.foldenable = true -- enable fold for nvim-ufo
    end,
    opts = {
      provider_selector = function(_, filetype, buftype)
        local function handleFallbackException(bufnr, err, providerName)
          if type(err) == "string" and err:match("UfoFallbackException") then
            return require("ufo").getFolds(bufnr, providerName)
          else
            return require("promise").reject(err)
          end
        end

        return (filetype == "" or buftype == "nofile") and "indent" -- only use indent until a file is opened
          or function(bufnr)
            return require("ufo")
              .getFolds(bufnr, "lsp")
              :catch(function(err) return handleFallbackException(bufnr, err, "treesitter") end)
              :catch(function(err) return handleFallbackException(bufnr, err, "indent") end)
          end
      end,
    },
  },
}

--------------------------------------------------------------------------------

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

test.js

export const Test = () => {
  return (
    <div>
      {/* A comment */}
      <div>
        <form
          id={FORMS_IDS.CHANGE_DESTINATION}
          onSubmit={createSaveData}
          sx={{ mt: 3 }}
        >
          <div className='my-class-name' />
        </form>
      </div>
    </div>
  )
}

Thanks!

Expected behavior

nvim shouldn't freeze

Actual behavior

nvim freezes

kevinhwang91 commented 7 months ago

Fixed. It looks like an upstream issue. You can insert :UfoDisable and %foldclose! between step 7 and step 8. And then open folds will know 4 line fold isn't closed.

serranomorante commented 7 months ago

Fixed. It looks like an upstream issue. You can insert :UfoDisable and %foldclose! between step 7 and step 8. And then open folds will know 4 line fold isn't closed.

Thank you for fixing this so quickly ❤