-
Notifications
You must be signed in to change notification settings - Fork 67
feat: summary status bar and improve process handling #114
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: main
Are you sure you want to change the base?
feat: summary status bar and improve process handling #114
Conversation
This PR started with a desire to add a summary status bar, but I quickly found
some improvements were required to command handling to make that happen.
- Improves visibility into the state of the extension by adding a
`Run On Save Status` status bar. Text of `running Sync: 3 Async: 2`
indicates that the extension is currently running 3 synchronous and 2 async
commands. Text of `idle Sync: 0 Async: 0` indicates that nothing is currently
running.
- Clicking the status bar reveals the Run On Save output.
- Removes vestigial status bar code that stopped working a long time ago.
- Improves synchronous command handling:
- Sync commands are processed one at a time in FIFO order. Prior to
this change, saving 20 files at the same time (like with a search/replace)
would lead to 20 separate sync commands being run
simultaneously. With this change, the 20 commands would be executed
one at a time.
- When disabled, terminates all running commands. This provides a
much-needed means to terminate commands when things have gone awry.
- The signal used to kill the processes is controlled via
command's `killSignal` property, defaulting to `SIGTERM`.
|
@bmingles My team makes heavy use of the run on save extension, and I'd really appreciate your feedback! Thank you very much! |
|
@justfalter Sorry for the delay. Lots of competing priorities. I'll try to take a look at the changes sometime this week when I can properly test. |
Thank you, @bmingles! I appreciate any time that you can give! |
bmingles
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.
@justfalter I've read through the diff and left a few comments.
I still need to actually test and make sure I fully understand the new queueing mechanism.
I also specifically want to test in Windows to make sure there are no surprises with the kill signal configurations since not all are supported by Windows.
Thanks again for the PR. I'm glad to have real users fixing real problems they are experiencing.
src/extension.ts
Outdated
| } | ||
|
|
||
| class RunOnSaveExtension { | ||
| interface runCommandConfig { |
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 interface should go in the model.ts file. Also, minor thing, but convention for types / interfaces is PascalCase
src/extension.ts
Outdated
| }); | ||
| } | ||
|
|
||
| public disable(): void { |
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'd also make this one async
| "error" | ||
| ] | ||
| }, | ||
| "killSignal": { |
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 need to test on Windows and make sure there aren't any footguns here since not all kill signals are supported there.
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.
Node's documentation for kill makes no mention of what it does for Windows platforms, but a look at Node's source code reveals that it will unconditionally terminate a process when SIGQUIT, SIGTERM, SIGINT, or SIGKILL are provided. Attempts to send any other signal appears to result in an error.
| const atLeastOneCommandIsRunning = unfinishedSyncCount !== 0 || asyncCount !== 0; | ||
|
|
||
| if (!this.isEnabled()) { | ||
| // If we are disabled but have unfinished commands, then we are "draining". |
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.
What's the scenario that can result in "draining"? Since commands are killed when extension is disabled, is there any meaningful time that we would see this status?
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 draining state simply reflects the knowledge that it can take time for a process to fully exit after receiving a termination signal. It's entirely possible that a process ignores a SIGTERM, so the draining state would allow the user to be aware of that instance.
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.
Ah makes sense. In that case, I'm good with the state, but I think I'd like to name it something a little more generic like "stopping" since I think "draining" may not be as broadly recognized.
bmingles
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.
@justfalter First of all, thank you for your contribution. I appreciate the help in improving the extension.
Inline Comments
I've done some testing and reviewed the changes a bit closer. I've left a handful of inline comments in the code.
Command Management Changes
Regarding the command management changes, there are 2 primary concerns I have related to changes in existing functionality.
- Parallel commands (
isAsync:true) that are configured after sequential ones (isAsync:false) used to wait until the preceeding sequential command completed. It seems that the parallel commands execute immediately now. They should wait for prior sequential commands to finish. For reference, I used this configuration to test:
"emeraldwalk.runonsave": {
// Messages to show before & after all commands
"message": "*** All Start ***",
"messageAfter": "*** All Complete ***",
// Show elappsed time for all commands
"showElapsed": true,
"commands": [
{
// This cmd should block all following commands (sequential and parallel ones)
"match": ".*",
"cmd": "sleep 3",
"message": "1. sleep 3"
},
{
// This cmd should wait until the first finishes
"match": ".*",
"cmd": "sleep 10",
"message": "2. sleep 10",
"isAsync": true
},
{
"match": ".*",
"cmd": "sleep 4",
"message": "3. sleep 4",
"isAsync": true
}
}- I think your observation that saving multiple files can cause many exec commands to fire at once is a good one and something I did not account for. My concern with the current changes is that it may impact existing users that are expecting the pre-existing behavior. For example, a user may actually want 20 exec commands to fire off in parallel across multiple files. Changing them to run sequentially across files could introduce unexpected lag in certain cases. I think it's a good fix, but it should be opt-in via a setting of some sort.
This does introduce some additional complexity since the command queue triggered by each file save will need to honor the rule mentioned in bullet 1 but also be able to order sequential commands across files if a user opts in to this feature.
Thanks again for the PR. Let me know if you need any clarification on my comments.
Co-authored-by: Brian Ingles <github@emeraldwalk.com>
You're completely right -- I'm not sure how/why I came to the conclusion that the async commands would fire immediately.
A global option to control the behavior would be fair. The only difference between the two modes would be the use of a single queue vs one per save event... I'll probably have to extract the queue out of the extension class...
Gladly. Everything you're saying so far makes sense to me! |
- Match prior behavior of only launching async commands when encountered in config order. We do not wait for an async command to complete before starting the next command.
|
@justfalter FYI, I merged a couple PRs that will require a rebase or merge down
|
This PR started with a desire to add a summary status bar, but I quickly found some improvements were required to command handling to make that happen.
Run On Save Statusstatus bar. Text ofrunning Sync: 3 Async: 2indicates that the extension is currently running 3 synchronous and 2 async commands. Text ofidle Sync: 0 Async: 0indicates that nothing is currently running.killSignalproperty, defaulting toSIGTERM.Description of Changes
Summarize changes this PR introduces.
Testing
Poke around with the status bar.
Have run on save config, verify that the status bar displays expected information.
Verify disabling extension terminates running commands.
The following command will spawn sync and async commands, each starting a "sleep":
{ "emeraldwalk.runonsave": { "commands": [ { "match": ".*", "cmd": "sleep 99999" }, { "match": ".*", "cmd": "sleep 88888", "isAsync": true } ] } }ps aux| grep sleepand observe the accumulation of asleep 99999sync command and severalsleep 88888async commands.Run On Save: Disablecommand.Checklist