Welcome to Vifm Q&A, where you can ask questions about using Vifm. Registration is optional, anonymous posts are moderated. E-mail and GitHub logins are enabled.
0 votes
in vifm by

Imagine I have a script script.sh that I want to run (not in the background), and after I interact with it, it prints some results. This could be a live grepping session for example.

I can create a command in vifmrc to put those results into a menu, and assign it a key binding:

command! script script.sh %m
nnoremap <c-s> :script<cr>

This works fine.

However, I would like to run something like that using the Lua API, because it would be easier to manipulate those results after I get them.

Currently, I understand the API exposes vifm.startjob which would let me gather command output, but runs in the background. So unless I open another terminal to run the script, I cannot interact with it. And if I open the other terminal, I cannot get the results of the script (for some reason):

local function live_grep_back(info)
    -- Command that works
    --local cmd = "echo 'one\ntwo\n'"
    -- My Script needs a terminal to be interactive
    local cmd = "kitty script.sh"
    local job = vifm.startjob {
        cmd = cmd,
        description = "My Script",
        visible = true,
        onexit = function(job)
          local i = 1
          local items = {}
          while true do
            local line = job:stdout():read()
            if line == nil then break end
            items[i] = line
            i = i + 1
          end
          vifm.menus.loadcustom({ title = "My Script Results", items = items, withnavigation = false })
        end
    }
    return true;
end

local added = vifm.cmds.add {
    name = "Script",
    description = "Run my script",
    handler = script,
    minargs = 0,
    maxargs = 0,
}

if not added then
    vifm.sb.error("Failed to register :Script")
end

return {}

If instead of vifm.startjob I use vifm.run, it seems I cannot get any results back. term() returns the contents of stdout, but vifm.run does not.

I wrote my own term() function for vifm and exposed it to Lua, and that seems to work. But before I try to submit a PR, I would like to know if I am missing something, and this can be done with the current toolset, without needing another function. Or maybe vifm.run could return the contents of stdout at exit.

1 Answer

+1 vote
by

Have you tried io.popen('script.sh', 'r')? It might actually need a wrapper to properly disable and re-enable TUI, but should otherwise work.

vifm.run() supports a bunch of macros which won't combine with returning output. vifm.startjob() doesn't block and can cause a mess if multiple jobs try to use the same terminal. So I'm leaning towards vifm.term() and/or vifm.system() if what's there isn't enough.

Relevant GitHub issue: https://github.com/vifm/vifm/issues/908

And if I open the other terminal, I cannot get the results of the script (for some reason)

The terminal must be catching the output.

by

Thanks a lot for your answer. I am sorry I didn't find the issue you mention, it was basically my question. I guess I mainly searched through the Q&A.

Using io.popen like you suggested does work. The only problem is the UI needs a refresh when coming back. I load a custom menu in this line:

...
vifm.menus.loadcustom({ title = "Script Results", items = items, withnavigation = false })

and the results are shown, but there is also some garbage on the screen because of the script having run before and the UI needing a frefresh.

Is there any way to do that from Lua?

by

There is no API for redrawing the UI. vifm.run() with a command that does nothing (echo can do to, don't know if all shells support : for a comment) can probably be abused for that:

vifm.run { cmd=':', usetermmux=false }
by

This works great, thank you!

In case it helps someone, I will leave a link to the final plugin:

shellfzf.vifm

by

Thank you and nice to see you here.
Seems that you are coming as well from yazi?

I was impressed about the many options vifm gave me right out the box, which I had to write plugins for in yazi. I like the „hackable“ approach of vimscripting and now going the same way with some bash scripts but try to avoid lua for now. ^^

by

vifm was my file manager for a very long time before it allowed plugins. There were a few things I was missing, and that is why I tried Yazi. The problem with Yazi is what you mention, I had to write a few plugins just to get some basic (for me) functionality I was already getting with vifm. With the new version of vifm, I could move everything back to vifm just by writing a few plugins/scripts to cover what I added to Yazi. Yazi is great, but it is not a dual pane manager, which I am used to, and the plugin I wrote for it was never going to work well, and required lots of updates as the API changed. Also, because Yazi doesn't natively support more than one pane, there were problems with background directory updates etc. I am also more of a C/C++ developer than Rust, so hacking the source code in vifm if I need to, is much easier for me. In the end, coming back to vifm really saves me a lot of time I can use for other projects :-)

by

In terms of api changing updates yazi is quite a newborn and vifm a lot more adult.

I would love to see more ideas in this "forum" and appreciate all your effort!

If you would like to make a bug report or feature request consider using GitHub, SourceForge or e-mail. Posting such things here is acceptable, but this is not a perfect place for them.
...