MagicDuck / grug-far.nvim

Find And Replace plugin for neovim
MIT License
832 stars 25 forks source link

FR: Allow ability to update existing named instances with prefills #165

Closed choovick closed 3 months ago

choovick commented 3 months ago

Maybe pushing it but here it goes.

Use case: I would like to have a persistent single instance of grug-far, but I would like to change scope of the search from nvim-tree re-opening or re-triggering it updating prefills

Example:

vim.keymap.set("n", "z", function()
    local node = lib.get_node_at_cursor()
    if node then
        -- get directory of current file if it's a file
        local path
        if node.type == "directory" then
            -- Keep the full path for directories
            path = node.absolute_path
        else
            -- Get the directory of the file
            path = vim.fn.fnamemodify(node.absolute_path, ":h")
        end

        -- open grug-far with the path
        require("grug-far").grug_far({
            prefills = {
                search = "",
                replacement = "",
                filesFilter = "",
                flags = "",
                paths = path,
            },
            instanceName = "far",
            staticTitle = "Find and Replace",
        })
    end
end, opts("Search in directory"))

Current behaviour: on grug_far: A grug-far instance with instanceName="far" already exists! on trigger: Just triggers, but prefills are not updated

Desired: on grug_far: buffer with instance name located, non empty prefills updated (this is basically a focus option as well) on trigger: tiggers, with prefills updated

MagicDuck commented 3 months ago

on grug_far: A grug-far instance with instanceName="far" already exists!

i am guessing if we kill the instance with that name and recreate it, that would achieve the desired effect. I need to do some thinking if we need separate api like instance_exists and close_instance or not.

on trigger: Just triggers, but prefills are not updated

By trigger I assume you mean toggle_instance call, correct?

choovick commented 3 months ago

i am guessing if we kill the instance with that name and recreate it, that would achieve the desired effect. I need to do some thinking if we need separate api like instance_exists and close_instance or not.

Yep, killing and re-creating will work, should you will loose other prefills, but thats ok for my use case.

on trigger: Just triggers, but prefills are not updated

Correct toggle_instance, should be cool if that supports prefills updates.

toggle_instance is great, but options like focus_instance, close_instance, update_instance would be really cool as well.

MagicDuck commented 3 months ago

@choovick FYI, still needs unit tests, but I've added some API in https://github.com/MagicDuck/grug-far.nvim/pull/172 I have leant more in the direction of adding specific functions that do simple things as opposed to making the existing ones more complex since that tends to be harder to deal with long term. Here's an excerpt from the help:

require('grug-far').has_instance({instanceName})               *grug-far.has_instance()*
        Checks if grug-far instance with given name exists

        Parameters: ~
            {instanceName}(string) 

    Return:
        {exists}(boolean) 

require('grug-far').is_instance_open({instanceName})           *grug-far.is_instance_open()*
        Checks if grug-far instance with given name is open.

        Parameters: ~
            {instanceName}(string) 

    Return:
        {is_open}(boolean) 

require('grug-far').close_instance({instanceName})           *grug-far.close_instance()*
        Closes (kills) grug-far instance with given name.

        Parameters: ~
            {instanceName}(string) 

require('grug-far').open_instance({instanceName})           *grug-far.open_instance()*
        Opens grug-far instance with given name if window is closed.
    Otherwise focuses the window.

        Parameters: ~
            {instanceName}(string) 

require('grug-far').update_instance_prefills({instanceName}, {prefills}, {clearOld})
                                                  *grug-far.update_instance_prefills()*
        Updates grug-far instance with given input prefill.
    If clearOld=true is given, the old input values are ignored.

        Parameters: ~
            {instanceName}(string) 
            {prefills}(PrefillsTableOverride) table 
           {search=, replacement=, filesFilter=, flags=, paths=}
            {clearOld}(boolean) if given, old input values are ignored

I did a quick test similar to what you have where each re-trigger increases a count that gets filled in and it seems to work:

      local count = 1
      vim.keymap.set('n', '<leader>so', function()
        local prefills = { search = 'something ' .. count }
        if not grugFar.has_instance('far') then
          grugFar.grug_far({
            instanceName = 'far',
            prefills = prefills,
          })
        else
          grugFar.open_instance('far')
          grugFar.update_instance_prefills('far', prefills, true)
        end

        count = count + 1
    end)

You can try the branch if you have time and let me know if you find any issue πŸ˜„ . I'll try to get to the tests soon...

choovick commented 3 months ago

api looks good, i'll play around with it now and will report.

choovick commented 3 months ago

https://github.com/user-attachments/assets/23e8bb04-e624-4bd8-8782-2a1924940933

vim.keymap.set("n", "z", function()
        local node = lib.get_node_at_cursor()
        local grugFar = require("grug-far")
        if node then
          -- get directory of current file if it's a file
          local path
          if node.type == "directory" then
            -- Keep the full path for directories
            path = node.absolute_path
          else
            -- Get the directory of the file
            path = vim.fn.fnamemodify(node.absolute_path, ":h")
          end

          local prefills = {
            paths = path,
          }

          if not grugFar.has_instance("tree") then
            grugFar.grug_far({
              instanceName = "tree",
              prefills = prefills,
              staticTitle = "Find and Replace from Tree",
            })
          else
            grugFar.open_instance("tree")
            -- updating the prefills without clearing the search
            grugFar.update_instance_prefills("tree", prefills, false)
          end
        end
      end, opts("Search in directory"))

works like a charm!

You need to slow down btw. Its not acceptable to have a repo with all issues addressed.

MagicDuck commented 3 months ago

great! Thanks for trying it out! Yeah, it's my first open source project and am also a bit OCD, haha. It's funny cause like a couple of months ago I was saying to myself that this project was pretty much complete and there was nothing more that could be added to it. I am always amazed how people find ways to use it. If it had only been me using it, it would have long stopped evolving πŸ˜† .

MagicDuck commented 3 months ago

it's merged now πŸ˜„ . Just FYI that I've also updated grug_far() and with_visual_selection() to return an automatically generated instanceName if one is not given (something like __grug_far_instance_<count>) as that can be preferable in some situations where you just want an unique instance and don't want to bother with the name. So the example above could be rewritten as:

      local count = 1
      local gfInstance
      vim.keymap.set('n', '<leader>so', function()
        local prefills = { search = 'something ' .. count }
        if not grugFar.has_instance(gfInstance) then
          gfInstance = grugFar.grug_far({ prefills = prefills })
        else
          grugFar.open_instance(gfInstance)
          grugFar.update_instance_prefills(gfInstance, prefills, true)
        end

        count = count + 1
      end)

Anyways, just one small request if you get a chance, could you create a PR to add a prettified example of your more advanced usage to the Cookbook section of the README.md?

MagicDuck commented 3 months ago

@choovick btw, one last note, if you want to support paths with spaces in their names you need to do this in your code snippet above:

path:sub(β€œ β€œ, β€œ\\ β€œ)
choovick commented 3 months ago

@choovick btw, one last note, if you want to support paths with spaces in their names you need to do this in your code snippet above:

path:sub(β€œ β€œ, β€œ\\ β€œ)

Yep, found that out pretty quickly! I will make PR for the README.md with my use case

MagicDuck commented 3 months ago

awesome, thanks! ❀️