ibhagwan / fzf-lua

Improved fzf.vim written in lua
MIT License
2.33k stars 151 forks source link

Custom fuzzy search with failover to last_query if no selections are found #524

Closed krisezra87 closed 2 years ago

krisezra87 commented 2 years ago

Description

This is continuing our brief discussion on reddit. The minimum working example problem is something like this: fuzzy search a directory for all files with a given extension; if you find a match, open the file; if you DO NOT find a match, create a file by the name of the last search query.

In reality, my problem is based around using rg to find markdown tags, but this is easier to share. What I have works, but I have no idea how to implement the "didn't find a match" case. This is the first day I've ever touched lua.

-- Define the folder where your files with the chosen extension are
zet_dir = "~/.vimwiki/zettelkasten/"

-- Define the separator used between words in the filenames
zet_word_sep = "_"

-- Define the extension
zet_ext = "md"

-- Enable searching through (and jumping to) existing zettels
_G.fzf_zettel_search = function(options)
    local fzf_lua = require'fzf-lua'
    options = options or {}
    options.prompt = "Zettels > "
    options.fzf_opts = { ['--print-query']=true }
    options.actions = {
        ['default'] = function(selected)
           -- Here's the place a conditional statement goes that I don't know how to write.  It's something like: selected[??] contains search?  Probably?
            -- search = require'fzf-lua.config'.__resume_data.last_query
            -- lua vim.cmd([[echom "]] .. require'fzf-lua.config'.__resume_data.last_query .. [["]])
            -- vim.cmd('echom "START"')
            -- vim.cmd('echom "' .. search .. '"')
            -- vim.cmd('echom "END"')
            choice = selected[1]:gsub(" ","_")
            vim.cmd("e " .. zet_dir .. choice .. "." .. zet_ext)
        end
    }
    options.cwd = zet_dir
    fzf_lua.fzf_exec([[fd -e ]] .. zet_ext .. [[ | sed 's/.*\///;s/\.md//;s/_/ /g']], options)
end

vim.keymap.set('n','<leader>zz','<cmd> lua _G.fzf_zettel_search()<cr>')

Sorry if I didn't follow the form closely enough, but this issue... isn't really an issue as much as it's a help request / potential feature suggestion.

Thanks!

krisezra87 commented 2 years ago

Oh shoot, I was dabbling with the line about --print-query based on what I found in core.lua, but I don't think it really helps anything here.

ibhagwan commented 2 years ago

This is the first day I've ever touched lua.

This is the beauty of not using vimscript, you feel right at home :-)

You were very close, all you need to do is check for an empty selected table:

_G.fzf_zettel_search = function(options)
    local fzf_lua = require'fzf-lua'
    options = options or {}
    options.prompt = "Zettels > "
    options.actions = {
        ['default'] = function(selected)
          local choice = nil
          if #selected > 0 then
            choice = selected[1]:gsub(" ","_")
          else
            choice = fzf_lua.config.__resume_data.last_query
          end
          local cmd = string.format("e %s/%s.%s", zet_dir, choice, zet_ext)
          print("cmd", cmd)
          vim.cmd(cmd)
        end
    }
    options.cwd = zet_dir
    fzf_lua.fzf_exec([[fd -e ]] .. zet_ext .. [[ | sed 's/.*\///;s/\.md//;s/_/ /g']], options)
end
ibhagwan commented 2 years ago

Btw I would also make zet_dir, zet_word_sep and zet_ext file locals, the way you currently defined it are as global variables.

ibhagwan commented 2 years ago

Also for a command that outputs files you can use fzf_lua.files({ cmd = 'fd …' }) (with all other options) so that you can enjoy the builtin file preview.

krisezra87 commented 2 years ago

Hey thanks! I will work this up properly.

W.r.t. the global variables, I have basically two main tasks: one that searches files for files, and another using rg that searches for words. I was looking for an elegant way to make those available to all functions in the file, but I didn't find a short answer for file-wide scope. Really, the next step will probably be wrapper functions that call the main block of code but change the options since really I just need to change the prompt and the action.

Thanks for looking this over!

Last thing though, if I use fzf_lua.files() I don't think it's going to work because I wanted to fuzzy search on words (i.e., the names of the files without extensions or underscores), the previewer can't handle that can it?

ibhagwan commented 2 years ago

Last thing though, if I use fzf_lua.files() I don't think it's going to work because I wanted to fuzzy search on words (i.e., the names of the files without extensions or underscores), the previewer can't handle that can it?

You can still do that, look into fzf's flag --nth, the default separator is : (configurable), you can include/exclude any field from the fuzzy matches while still keeping full paths in the display so the previewer can work.

If that doesn't work you can always write your own custom previewer that parses the entry based on your own logic, as long as you can deduce the filename from the entry the previewer can work.

krisezra87 commented 2 years ago

Perfect thanks! I'm going to close this up, you've done way more than I would have expected from a maintainer :).

ibhagwan commented 2 years ago

Perfect thanks! I'm going to close this up, you've done way more than I would have expected from a maintainer :).

Thanks @krisezra87! don't hestiate to reopen if you need anything.