-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Adding lua function to use result of RunBackgroundShell #3692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Adding lua function to use result of RunBackgroundShell #3692
Conversation
|
I do not know what use cases does local micro = import("micro")
local shell = import("micro/shell")
local job
local function onexit(output, data)
local state = job.ProcessState
-- ProcessState is set if process (sh if using JobStart) exits
-- onExit is called even if an error occurs with something like I/O within
-- Go, but it is probably rare
--if state == nil then return end
micro.TermMessage(state:Success() and output or state:String())
end
function init()
job = shell.JobStart("kill $$", nil, nil, onexit)
endI think it is better to instead change the function so that it returns an error when occuring on process startup, and passes the error that |
|
The main thing I wanted was to able to do something like Yeah it's true that I can do But even then you still need to keep track of the command execution by storing it in global (probably in a map or some sort?) If this PR gets merged, what you need to do instead it's just local function onexit(output, err)
-- Handle err here...
micro.TermMessage(output)
end
function init()
local retFunc, err = shell.RunBackgroundShell("curl whatever")
-- Handle err...
shell.LaunchBackgroundShellFunc(retFunc, onexit)
endwhere you can handle the error directly in the callback as param, not to mention it has a simpler interface than In my mind, the Job* functions make sense for long-running tasks like LSP but overkill and painful to manage for a command that takes a few seconds to complete. |
Yes, that is right. Tables in local t = { i = 1 }
-- in onexit, userargs[1] is { i = 1 } with job not set
t.job = shell.JobStart("sleep 1", nil, nil, onexit, t)
t.i = 2There are workarounds, so I don't think changing the function to pass Lua values without conversion needs to be prioritized. Arrays converted using gopher-luar can be iterated by writing
I agree that the interface is simpler, especially since no objects have to be created in plugins to check errors. However, the interface seems a bit odd since the names are similar even though A function that's like a simplified version of
I have no experience with adding APIs so I'll leave this to maintainers or others to decide and discuss. |
I think you misunderstood what I meant. I meant you need to keep track of the
Yes, I totally agree that
Yeah I would like to do that but I feel like that will be confusing as well since that will be doing what Honestly though, I am happy to modify But I think at the minimum, we should at least merge the fix for |
Sorry, I meant to mention another approach on keeping track of the job object, that currently doesn't work even though it would be better than using something like a map in some cases.
I think it's better to submit this change in another pull request even if it's a lot smaller. I have done something similar in #3672 but I honestly have other pull requests where I haven't done such yet. |
|
Regarding problems with Regarding this:
You could use a closure: local function onexit(output, job)
local state = job.ProcessState
micro.TermMessage(state:Success() and output or state:String())
end
function init()
local job
job = shell.JobSpawn("date", {}, nil, nil, function(output)
onexit(output, job)
end)
endIt works, but it is actually incorrect, because it is racy: the background command may have already finished (and thus @niten94 's example in #3692 (comment) has the same problem, I suppose. So it appears that ...I agree that the |
Alright, I think that would be good.
I don't see how
Plugins can also set PWD and variables after calling I've came up with naming the latter function I don't see any advantages in exporting |
Ah, indeed... So, it is not So, a corrected version: local function onexit(output, job)
local state = job.ProcessState
if state ~= nil then
micro.TermMessage(state:Success() and output or state:String())
else
micro.TermMessage("failed to execute date")
end
end
function init()
local job
job = shell.JobSpawn("date", {}, nil, nil, function(output)
onexit(output, job)
end)
endIt works reliably, but it doesn't allow to report the exact reason why it failed to execute... |
03031e7 to
4658fa2
Compare
4658fa2 to
c626395
Compare
internal/action/command.go
Outdated
| output = fmt.Sprint(args[0], " exited with error: ", runErr) | ||
| } | ||
| InfoBar.Message(output) | ||
| screen.Redraw() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The screen doesn't need to be redrawn here now, since the event loop does it after the callback is ran.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
internal/shell/shell.go
Outdated
| errVal, ok := args[0].(error) | ||
| if ok { | ||
| cb(output, errVal) | ||
| } else { | ||
| cb(output, nil) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to directly run the callback without checking ok, and assign the boolean result to _ instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. Updated.
internal/shell/shell.go
Outdated
| // RunBackgroundCommand runs a shell command in the background by accepting | ||
| // an input for running the command and an optional callback function for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To match the style of other comments in Micro and be slightly clearer about behavior, it may be better to write the description as something like this: "RunBackgroundCommand runs a shell command and an optional callback on exit..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I have shortened the comment.
runtime/help/plugins.md
Outdated
| - `RunBackgroundCommand(input string, cb func(string, error)) error`: | ||
| runs a shell command in the background by accepting an input for | ||
| running the command and an optional callback function for the | ||
| command output or error if any. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be written instead that the function is the same as ExecBackgroundCommand() and parses the string argument like RunCommand(), to avoid adding similar text while referencing a slightly detailed explanation on the parsing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RunBackgroundCommand is different since it returns an error. I updated the docs to point to the non-background equivalents.
internal/shell/shell.go
Outdated
| go func() { | ||
| output, runErr := ExecCommand(name, args[0:]...) | ||
| if cb != nil { | ||
| wrapperFunc := func(output string, args []interface{}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interface{} was replaced with any in all parts of Micro's code in #3834, so it should be done with this PR as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, updated to use any now.
|
I'm sorry for the slightly confusing comments I left before and not leaving any review. I've slightly used and read about |
1bfb8e3 to
f870350
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only documentation suggestions at this point, but this PR should be good.
I should've mentioned this a lot earlier but I honestly avoided reviewing this PR, because I had been thinking a new shell API could replace most of the existing and added functions.
After thinking more, I think a new plugin API should be introduced as a whole instead. I plan to create an issue for it sometime and the old API would only always remain deprecated. Still, it's unlikely a large new API can be introduced around this time.
runtime/help/plugins.md
Outdated
| - `ExecBackgroundCommand(cb func(string, error), name string, args ...string)`: | ||
| similar to ExecCommand except it runs in the background and accept |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This applies to the plugin documentation of RunBackgroundCommand() as well, but it's better to enclose "ExecCommand" in backticks because this is markdown text. However, identifiers wouldn't be quoted in Go comments.
internal/shell/shell.go
Outdated
| // ExecBackgroundCommand is similar to ExecCommand except it runs in the | ||
| // background and accept an optional callback function for the command output | ||
| // or error if any. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This applies to the description in plugin.md as well, but comma could be added before "except" and "accept" should be replaced with "accepts".
Also, I think the comment will be slightly clearer and consistent if "if any." (including period) is removed.
Edit: Instead of my suggestion in the previous sentence, replacing the last line with "or an error if any" (and still removing the period) might be better.
runtime/help/plugins.md
Outdated
| - `RunBackgroundCommand(input string, cb func(string, error)) error`: | ||
| similar to RunCommand except it runs in the background and accept | ||
| an optional callback function for the command output or error if any. | ||
| It returns an error immediately if it fails to split the input command |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Period should probably be added here at the end like other function descriptions in plugin documentation.
internal/shell/shell.go
Outdated
| // RunBackgroundCommand is similar to RunCommand except it runs in the | ||
| // background and accept an optional callback function for the command output | ||
| // or error if any. | ||
| // It returns an error immediately if it fails to split the input command |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar with the suggestion of the comment on ExecBackgroundCommand(). It may also slightly look better to replace "It returns" with "Returns" in the comment only.
Using Jobs channel to call lua callback
f870350 to
2209957
Compare
niten94
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I apologize that my previous comment is a bit incosiderate, including the idea of deprecating APIs which isn't a good idea too.
|
No worries. I was just a bit busy recently and hence the late edit. |
Currently,
RunBackgroundShell()returns a function and an error. I don't think there's a way to run that function asynchronously from lua plugin.Adding a helper lua function for that.
Although there's an additional error to the return of the returned function from
RunBackgroundShell(), this has no impact the lua plugins that are currently using it since it first return var is the same. I have tested this with a lua plugin.