folke / flash.nvim

Navigate your code with search labels, enhanced character motions and Treesitter integration
Apache License 2.0
2.4k stars 30 forks source link

Cursor jumps to first target in operator-pending mode for char movements #204

Open TxHawks opened 1 year ago

TxHawks commented 1 year ago

Discussed in https://github.com/folke/flash.nvim/discussions/186

I initially opened this as a discussion, but I do think this is a bug, unless I'm missing something

Originally posted by **TxHawks** July 26, 2023 When I use char-mode movements in operator-pending mode, e.g., `df`, the cursor will jump to the first instance of `` and will then carry out the operator-pending action - in this case, delete. So if I have my cursor at the beginning of the following line: ``` A random line of text ``` and then hit `dte`, the cursor will to the `n` in `line`, and delete from there to the label I pick or the first one if I just hit `enter`. Is this a bug or do I have something misconfigured? This is my config: ```lua { incremental = true, jump = { -- Disabling this was the first thing I tried. Didn't help autojump = false, }, label = { current = false, }, modes = { char = { jump_labels = true, label = { exclude = "abcdehijklrxw" }, }, }, search = { incremental = true, }, }, ```
nullromo commented 10 months ago

I am having the same issue. As-is, flash is "breaking" the functionality of ct<character>. I think the fix would be to disable flash's "char mode" when in operator pending mode. I tried the following, but it did not work:

opts = {
    modes = {
        char = {
            config = function(opts)
                if vim.fn.mode(true):find('o') then
                    vim.notify('Started flash char jump from operator pending mode')
                    -- these options seem to have no effect in this context
                    opts.enabled = false
                    opts.autohide = true
                else
                    vim.notify('Started flash char jump from normal mode')
                end
            end,
        },
    },
},

In normal mode, the notify calls are printing correctly when I press t vs. ct. However, the behavior is the same. Flash takes over and jumps to the next letter after ct rather than doing the default vim behavior of deleting up to that character and entering insert mode.

mrbeardad commented 9 months ago

autohide is what you want, but it only work when jump_labels is false. So, you have to disable jump_labels too in operator-pending mode.

{
    modes = {
        char = {
            highlight = { backdrop = false },
            config = function(opts)
                opts.autohide = vim.fn.mode(true):find("no")
                -- Show jump labels not in operator-pending mode
                opts.jump_labels = not vim.fn.mode(true):find("o")
                    and vim.v.count == 0
                    and vim.fn.reg_executing() == ""
                    and vim.fn.reg_recording() == ""
            end,
        },
    },
}
Jendker commented 9 months ago

That's great! I only wish there was a way to avoid momentary blink of the labels after pressing char in operator-pending mode.

nullromo commented 9 months ago

@mrbeardad It still doesn't work. I have had jump_labels set to false the whole time. ct<character> still just jumps forward to that character and doesn't allow the regular c operator to function properly.

mrbeardad commented 9 months ago

@nullromo What is your config?

nullromo commented 9 months ago

You can see my config here. The relevant part is copied below. jump_labels is false by default.

opts = {
    label = {
        -- only use lowercase letters for jump labels
        uppercase = false,
        -- use <CR> as the first label
        current = false,
        -- position the labels at the beginning of the search words
        after = false,
        before = true,
    },
    highlight = {
        backdrop = false,
    },
    modes = {
        search = {
            enabled = false,
        },
        char = {
            autohide = false,
            search = {
                wrap = true,
            },
            highlight = {
                backdrop = false,
                matches = false,
                groups = {
                    label = "FlashMatch",
                },
            },
            char_actions = function(motion)
                return {
                    [motion:lower()] = 'right',
                    [motion:upper()] = 'left',
                }
            end,
        },
    },
    remote_op = {
        motion = true,
    },
},
mrbeardad commented 9 months ago
opts = {
    label = {
        -- only use lowercase letters for jump labels
        uppercase = false,
        -- use <CR> as the first label
        current = false,
        -- position the labels at the beginning of the search words
        after = false,
        before = true,
    },
    highlight = {
        backdrop = false,
    },
    modes = {
        search = {
            enabled = false,
        },
        char = {
-            autohide = false,
+            autohide = true,
            search = {
                wrap = true,
            },
            highlight = {
                backdrop = false,
                matches = false,
                groups = {
                    label = "FlashMatch",
                },
            },
            char_actions = function(motion)
                return {
                    [motion:lower()] = 'right',
                    [motion:upper()] = 'left',
                }
            end,
        },
    },
    remote_op = {
        motion = true,
    },
},
nullromo commented 9 months ago
  1. When autohide is true, the flash functionality of f and t turns off. The point is to be able to press, for example, fcffffff... to cycle through occurrences of the letter c with them all highlighted instead of pressing fcfcfcfcfc with no highlighting.

  2. It doesn't even work anyway. With autohide set to true, I still can't ct<character>. It has the same behavior as setting autohide to false.

Maybe it's something else in my config that's causing it to fail, so I will try to use a minimal setup to check.

nullromo commented 9 months ago

I followed the steps below and confirmed that this is reproducible with a miniaml setup.

  1. Create a file (touch test.lua)
  2. Write the following contents to the file:
    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.rtp:prepend(lazypath)
    local plugins = {
    {
        'folke/flash.nvim',
        --
        -- rest of the flash config stuff here ...
        --
    }
    }
    require('lazy').setup(plugins, {})
  3. Launch vim with this as the config (vim -u test.lua test.lua)
  4. Confirm that flash is working (flffff...)
  5. Test the problem (ct<character>)

This confirms that it's not some other part of my vim config that's causing the problem.

mrbeardad commented 9 months ago
opts = {
  label = {
    -- only use lowercase letters for jump labels
    uppercase = false,
    -- use <CR> as the first label
    current = false,
    -- position the labels at the beginning of the search words
    after = false,
    before = true,
  },
  highlight = {
    backdrop = false,
  },
  modes = {
    search = {
      enabled = false,
    },
    char = {
-     autohide = false,
+     config = function(opts)
+       -- autohide flash when in operator-pending mode
+       opts.autohide = vim.fn.mode(true):find("no")
+     end,
      search = {
        wrap = true,
      },
      highlight = {
        backdrop = false,
        matches = false,
        groups = {
          label = "FlashMatch",
        },
      },
      char_actions = function(motion)
        return {
          [motion:lower()] = "right",
          [motion:upper()] = "left",
        }
      end,
    },
  },
-   remote_op = {
-     motion = true,
-   },
}

For detail on remote_op, refer to the readme

nullromo commented 9 months ago

Removing remote_op = { motion = true } made it work. I tried setting motion to true and false back and forth and I couldn't figure out what that setting even does. But with the default motion = false, ct<character> works correctly 🎉

I'm not sure why the remote_op settings would affect anything if require('flash').remote isn't being run though...

siriobalmelli commented 7 months ago

I'm also having trouble with this. Adding this config entry does not fix the problem:

   remote_op = {
     motion = false,
   },

My current config (with character motion disabled):

vim.opt.incsearch = true
vim.opt.hlsearch = false

require('flash').setup({
   search = {
      incremental = true,
   },
   modes = {
      char = {
         -- https://github.com/folke/flash.nvim/issues/204
         enabled = false,
      },
   },
})

function FlashSearch()
   require('flash').jump({ pattern = vim.fn.expand("<cword>"), mode = "search" })
end

vim.api.nvim_set_keymap('n', '#',
   [[:lua FlashSearch()<CR>]],
   { noremap = true, silent = true })

vim.api.nvim_set_keymap('n', '?',
   [[:lua require("flash").treesitter()<CR>]],
   { noremap = true, silent = true })

I cannot currently find a way to go from this config to a config where character motions work. I have tried:

require('flash').setup({
   search = {
      incremental = true,
   },
   modes = {
      char = {
         enabled = true,
         jump_labels = false,
         autohide = true,
      },
   },
   remote_op = {
      motion = false,
   },
})

or:

      char = {
         enabled = true,
         jump_labels = false,
         autohide = true,
      },

or:

      char = {
         enabled = true,
         config = function(opts)
            opts.autohide = vim.fn.mode(true):find("no")
            -- Show jump labels not in operator-pending mode
            opts.jump_labels = not vim.fn.mode(true):find("o")
                and vim.v.count == 0
                and vim.fn.reg_executing() == ""
                and vim.fn.reg_recording() == ""
         end,
      },

or:

      char = {
         jump_labels = false,
         autohide = true,
         highlight = { backdrop = false },
         config = function(opts)
            opts.autohide = vim.fn.mode(true):find("no")
            -- Show jump labels not in operator-pending mode
            opts.jump_labels = not vim.fn.mode(true):find("o")
                and vim.v.count == 0
                and vim.fn.reg_executing() == ""
                and vim.fn.reg_recording() == ""
         end,
      },
Jendker commented 7 months ago

@siriobalmelli I started using clever-f again instead of 'char' mode in flash.nvim and that's exactly the behavior I need.

  {
    'rhysd/clever-f.vim',
    event = 'VeryLazy',
    config = function()
      vim.g.clever_f_smart_case = 1
    end,
  },
  {
    "folke/flash.nvim",
    event = "VeryLazy",
    opts = {
      modes = {
        char = {
          enabled = false,
        },
      },
    },
github-actions[bot] commented 2 months ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.

nullromo commented 2 months ago

Has not been addressed, so is not stale.

github-actions[bot] commented 1 month ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.

Jendker commented 1 month ago

Bump

siriobalmelli commented 1 month ago

Bump

github-actions[bot] commented 1 day ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.