-
Notifications
You must be signed in to change notification settings - Fork 896
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?
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.Quit
If 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.
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!
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!
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
,RichSelect
andMultiSelect
do 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 select
Select
go run ./cmd/coder exp prompt-example select
Select
go run ./cmd/coder exp prompt-example multiple
Select
,MultiSelect
go run ./cmd/coder exp prompt-example multi-select
MultiSelect
go run ./cmd/coder exp prompt-example rich-parameter
RichSelect
cdr templates delete
(against a local instance)Select
cdr orgs roles edit organization-auditor
(against a local instance)Select
,MultiSelect
cdr create -t devcontainer-docker
(against a local instance)RichSelect