-
Notifications
You must be signed in to change notification settings - Fork 1.1k
chore: replace AlecAivazis/survey with charmbracelet/bubbletea #14475
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
Conversation
88d286f to
2b063b0
Compare
cli/cliui/select.go
Outdated
| func (m multiSelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | ||
| var cmd tea.Cmd | ||
|
|
||
| if msg, ok := msg.(tea.KeyMsg); ok { |
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.
nit: invert and early return
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've slightly changed the logic here. I just noticed it is slightly different to how the func (m selectModel) Update function works. m.search.Update can handle more than just a tea.KeyMsg. With the updated logic does this still make sense?
mafredri
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.
Nice work, and thanks for the asciinemas! Left a few comments on changes I'd like to see in all functions/models, but otherwise looks good.
cli/cliui/select.go
Outdated
| } | ||
|
|
||
| func (selectModel) Init() tea.Cmd { | ||
| return textinput.Blink |
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 found the blinking fairly distracting in the aciinemas, that behavior seemed different from the original. Is this what controls it and should we actually blink?
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.
Yes, this is what controls it https://pkg.go.dev/github.com/charmbracelet/bubbles@v0.19.0/textinput#Blink.
I'm happy to back it out
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.
Doing some more research, it appears that the textinput component also triggers the blinking behaviour
cli/cliui/select.go
Outdated
| func (m selectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | ||
| var cmd tea.Cmd | ||
|
|
||
| if msg, ok := msg.(tea.KeyMsg); ok { |
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 happens if the program receives a kill -INT $PID while tea is active? Do we handle it nicely?
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.
Unless I'm doing something wrong, it appears nothing happens?
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.
Hmm, did you try with a coder command or cliui test binary? With coder I would expect the program to exit with a cancelled.
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 used the cliui binary with go run ./cmd/cliui select. I shall try against the coder binary
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 checked on main and this behaviour appears to be the status quo.
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.
Just tested and it does not! Need to handle that then, err is 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.
So... Bubbletea listens for SIGINT and SIGTERM signals and emits a QuitMsg command
https://github.com/charmbracelet/bubbletea/blob/e58efab3713e8f924e90a416e66326b23253f7ab/tea.go#L238-L272
The issue is, the event loop finishes on a QuitMsg command without notifying our Update function.
https://github.com/charmbracelet/bubbletea/blob/e58efab3713e8f924e90a416e66326b23253f7ab/tea.go#L352-L355
We could potentially install our own signal handlers by disabling the builtin one
https://pkg.go.dev/github.com/charmbracelet/bubbletea#WithoutSignalHandler
https://github.com/charmbracelet/bubbletea/blob/e58efab3713e8f924e90a416e66326b23253f7ab/tea.go#L508-L511
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 can confirm by disabling the builtin signal handler it is possible to get err to contain the Canceled error.
I wrote a small signal handler
type terminateMsg struct{}
func installSignalHandler(p *tea.Program) chan struct{} {
ch := make(chan struct{})
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
defer func() {
signal.Stop(sig)
close(ch)
}()
for {
select {
case <-ch:
return
case <-sig:
p.Send(terminateMsg{})
}
}
}()
return ch
}Then install it on a *tea.Program
ch := installSignalHandler(p)
defer func() {
ch <- struct{}{}
}()And handle it in the Update methods
switch msg := msg.(type) {
case terminateMsg:
m.canceled = true
return m, tea.QuitIf this seems okay I can push this change? @mafredri
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.
Might I suggest returning a func instead of the channel for close/deregistering the handler? I feel the usage will be a bit more idiomatic/easy to understand.
On the other hand, perhaps we don't need to handle signals at all and leave it to be the responsibility of the coder command that needs it? I guess it's possible the terminal is left in a bad state unless the signal is handled though (not sure if it enters raw mode or not).
PS. You might want to only handle signal.Interrupt for the Windows compatibility.
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.
Yes, definitely a good idea. Have made that change 👍
Killing the process with kill -TERM $PID leaves the cursor in a bad state if we don't explicitly handle it (syscall.SIGTERM).
Will push these changes up, can always revert if unhappy.
johnstcn
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.
Nice work and thorough testing!
mafredri
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 after the conflicts are resolved, nice work!
mtojek
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 👍 Feel free to merge once you update flake.nix go.mod and make CI happy. Nice contribution!
7f32e05 to
6d87a98
Compare
6d87a98 to
966f772
Compare
|
Late to the party, but one thing that bothered me with the |
This absolutely can be changed now! Personally I prefer the question (with the answer) remaining, so I'd be curious if there is any way of finding out if that would be a positive/negative change amongst our customers. |
fixes #12720
This PR replaces the unmaintained https://github.com/AlecAivazis/survey library with https://github.com/charmbracelet/bubbletea.
The existing tests for the components
Select,RichSelectandMultiSelectdo not test anything meaningful as the components short-circuit to return the first option when ran in a test and the tests just confirm this short-circuiting behaviour. Future work will be to remove this short-circuiting behaviour and fix the tests.I've manually tested the behaviour of the components with:
go run ./cmd/cliui selectSelectgo run ./cmd/coder exp prompt-example selectSelectgo run ./cmd/coder exp prompt-example multipleSelect,MultiSelectgo run ./cmd/coder exp prompt-example multi-selectMultiSelectgo run ./cmd/coder exp prompt-example rich-parameterRichSelectcdr templates delete(against a local instance)Selectcdr orgs roles edit organization-auditor(against a local instance)Select,MultiSelectcdr create -t devcontainer-docker(against a local instance)RichSelect