Skip to content

Commit 14390d1

Browse files
author
jlanzarotta
committed
fix: attempting fix prompt hanging issue
From time to time, when Khronos asks for user input, the program seems to hang. After further investigation, it might have to do with how I am reading information from the terminal. I have made a few changes, so we will see what happens.
1 parent 16861a4 commit 14390d1

6 files changed

Lines changed: 80 additions & 52 deletions

File tree

cmd/add.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ func runAdd(cmd *cobra.Command, args []string) {
138138
}
139139

140140
var projectTask string = constants.EMPTY
141+
var description string = constants.EMPTY
141142
var ticket string = constants.EMPTY
142143
var requiredNote bool = false
143144

@@ -148,6 +149,7 @@ func runAdd(cmd *cobra.Command, args []string) {
148149
// "#" column); getFavorite indexes 0-based, so convert here.
149150
var fav Favorite = getFavorite(favorite - 1)
150151
projectTask = fav.Favorite
152+
description = fav.Description
151153
ticket = fav.Ticket
152154
requiredNote = fav.RequireNote
153155
} else {
@@ -180,6 +182,7 @@ func runAdd(cmd *cobra.Command, args []string) {
180182

181183
var fav Favorite = getFavorite(idx)
182184
projectTask = fav.Favorite
185+
description = fav.Description
183186
ticket = fav.Ticket
184187
requiredNote = fav.RequireNote
185188
}
@@ -197,7 +200,7 @@ func runAdd(cmd *cobra.Command, args []string) {
197200
if stringUtils.IsEmpty(note) {
198201
var globalRequired bool = viper.GetBool(constants.REQUIRE_NOTE)
199202
if globalRequired || requiredNote {
200-
note = promptForNote(projectTask, true)
203+
note = promptForNote(projectTask, description, true)
201204

202205
// If the note is still empty, this is an indicator that the user wants to exit.
203206
if len(note) <= 0 {
@@ -235,8 +238,7 @@ func runAdd(cmd *cobra.Command, args []string) {
235238
}
236239
}
237240

238-
func promptForNote(projectTask string, required bool) string {
239-
r := bufio.NewReader(os.Stdin)
241+
func promptForNote(projectTask string, description string, required bool) string {
240242
var s string
241243
var prompt string
242244

@@ -249,6 +251,14 @@ func promptForNote(projectTask string, required bool) string {
249251
prompt += color.YellowString("Task")
250252
prompt += "["
251253
prompt += pieces[1]
254+
255+
if !stringUtils.IsEmpty(description) {
256+
prompt += "] "
257+
prompt += color.YellowString("Description")
258+
prompt += "["
259+
prompt += description
260+
}
261+
252262
prompt += "] requires a note...\n"
253263
} else {
254264
prompt = "A note is required...\n"
@@ -257,8 +267,42 @@ func promptForNote(projectTask string, required bool) string {
257267
prompt += "Enter note or leave blank to quit. > "
258268

259269
fmt.Print(prompt)
260-
s, _ = r.ReadString('\n')
270+
s, _ = readLine(stdinReader)
261271
s = strings.TrimSpace(s)
262272

263273
return s
264274
}
275+
276+
// readLine reads a single line of input from r, terminated by '\n', '\r',
277+
// or '\r\n'. Unlike bufio.Reader.ReadString('\n'), this tolerates a bare
278+
// '\r' - which is what a raw-mode terminal (e.g. one left in that state by
279+
// a Bubble Tea program that hasn't fully restored cooked mode yet) sends
280+
// for the Enter key instead of '\n'. Without this, ReadString('\n') can
281+
// block forever waiting for a byte that never arrives.
282+
func readLine(r *bufio.Reader) (string, error) {
283+
var sb strings.Builder
284+
285+
for {
286+
b, err := r.ReadByte()
287+
if err != nil {
288+
// Return whatever we've accumulated so far (e.g. EOF mid-line).
289+
return sb.String(), err
290+
}
291+
292+
if b == '\n' {
293+
return sb.String(), nil
294+
}
295+
296+
if b == '\r' {
297+
// Peek ahead in case this is a "\r\n" pair; if so, consume the
298+
// '\n' too so it doesn't leak into the next read.
299+
next, err := r.Peek(1)
300+
if err == nil && len(next) == 1 && next[0] == '\n' {
301+
_, _ = r.ReadByte()
302+
}
303+
return sb.String(), nil
304+
}
305+
306+
sb.WriteByte(b)
307+
}
308+
}

cmd/amend.go

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ POSSIBILITY OF SUCH DAMAGE.
3131
package cmd
3232

3333
import (
34-
"bufio"
35-
36-
//FIXME "database/sql"
3734
"fmt"
3835
"khronos/constants"
3936
"log"
@@ -184,17 +181,12 @@ func runAmend(cmd *cobra.Command, _ []string) {
184181
}
185182

186183
func prompt(label string, value string) string {
187-
var s string
188-
scanner := bufio.NewScanner(os.Stdin)
189184
fmt.Fprintf(os.Stderr, "Enter %s (empty for no change) ["+value+"] > ", label)
190-
if !scanner.Scan() {
191-
s = scanner.Text()
185+
s, _ := readLine(stdinReader)
186+
s = strings.TrimSpace(s)
192187

193-
// If the result is empty, use the original passed in value.
194-
if s == constants.EMPTY {
195-
s = value
196-
}
197-
} else {
188+
// If the result is empty, use the original passed in value.
189+
if s == constants.EMPTY {
198190
s = value
199191
}
200192
return s

cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
3131
package cmd
3232

3333
import (
34+
"bufio"
3435
"errors"
3536
"khronos/constants"
3637
"khronos/internal/database"
@@ -51,6 +52,7 @@ import (
5152
var cfgFile string
5253
var note string
5354
var terminalWidth int
55+
var stdinReader = bufio.NewReader(os.Stdin)
5456

5557
// rootCmd represents the base command when called without any subcommands
5658
var rootCmd = &cobra.Command{

cmd/stretch.go

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ POSSIBILITY OF SUCH DAMAGE.
3131
package cmd
3232

3333
import (
34-
"bufio"
3534
"fmt"
3635
"khronos/constants"
3736
"log"
@@ -111,24 +110,16 @@ func runStretch(cmd *cobra.Command, _ []string) {
111110
}
112111

113112
func yesNoPrompt(format string, args ...any) bool {
114-
scanner := bufio.NewScanner(os.Stdin)
115-
var s string
116-
117-
for {
118-
fmt.Printf(format, args...)
119-
fmt.Print(" Y/N (yes/no) > ")
120-
if !scanner.Scan() {
121-
return false
122-
}
123-
s = scanner.Text()
124-
s = strings.ToLower(s)
125-
switch s {
126-
case "y", "yes":
127-
return true
128-
case "n", "no":
129-
return false
130-
default:
131-
return false
132-
}
113+
fmt.Printf(format, args...)
114+
fmt.Print(" Y/N (yes/no) > ")
115+
116+
s, _ := readLine(stdinReader)
117+
s = strings.ToLower(strings.TrimSpace(s))
118+
119+
switch s {
120+
case "y", "yes":
121+
return true
122+
default:
123+
return false
133124
}
134125
}

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
github.com/ijt/go-anytime v1.9.2
1111
github.com/mattn/go-isatty v0.0.22
1212
github.com/mattn/go-runewidth v0.0.24
13-
golang.org/x/term v0.44.0
13+
golang.org/x/term v0.45.0
1414
modernc.org/sqlite v1.53.0
1515
)
1616

@@ -38,9 +38,9 @@ require (
3838
github.com/rivo/uniseg v0.4.7 // indirect
3939
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
4040
go.yaml.in/yaml/v3 v3.0.4 // indirect
41-
golang.org/x/crypto v0.53.0 // indirect
41+
golang.org/x/crypto v0.54.0 // indirect
4242
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
43-
modernc.org/libc v1.74.0 // indirect
43+
modernc.org/libc v1.74.1 // indirect
4444
modernc.org/mathutil v1.7.1 // indirect
4545
modernc.org/memory v1.11.0 // indirect
4646
)
@@ -62,6 +62,6 @@ require (
6262
github.com/spf13/viper v1.21.0
6363
github.com/subosito/gotenv v1.6.0 // indirect
6464
golang.org/x/sys v0.47.0 // indirect
65-
golang.org/x/text v0.39.0 // indirect
65+
golang.org/x/text v0.40.0 // indirect
6666
gopkg.in/yaml.v3 v3.0.1
6767
)

go.sum

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc
7575
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
7676
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
7777
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
78-
github.com/pelletier/go-toml/v2 v2.4.2 h1:M2fKKbmyvI+hGId/D0W64qDBMVhJnNR10O5gIbMc//Q=
79-
github.com/pelletier/go-toml/v2 v2.4.2/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
8078
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
8179
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
8280
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
@@ -115,37 +113,38 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
115113
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
116114
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
117115
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
116+
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
117+
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
118118
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
119119
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
120120
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
121121
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
122122
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
123123
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
124+
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
124125
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
125126
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
126-
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
127-
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
128127
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
129128
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
130129
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
131130
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
132-
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
133-
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
131+
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
132+
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
134133
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
135134
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
136-
golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk=
137-
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
135+
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
136+
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
138137
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
138+
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
139139
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
140140
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
141141
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
142142
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
143143
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
144144
modernc.org/cc/v4 v4.29.0 h1:CXgwL8cvxmyzBQZzbSl/6xFtMCryb6u8IOqDci39cgc=
145145
modernc.org/cc/v4 v4.29.0/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
146-
modernc.org/ccgo/v4 v4.34.5 h1:hcwnthv2/LBl+mRLOYwnQA/LuW44Oln1NQlWppNaS1Q=
147-
modernc.org/ccgo/v4 v4.34.5/go.mod h1:aow0HNkO30OSA/2NrtDXkis92ff8ZFiDOmDOPhqhF8U=
148146
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
147+
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
149148
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
150149
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
151150
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
@@ -154,10 +153,10 @@ modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
154153
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
155154
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
156155
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
157-
modernc.org/libc v1.73.5 h1:G34rN/cRqL+zOUnrbz9uPq/+OxJ8/vzQ2CQwTJ42Wmw=
158-
modernc.org/libc v1.73.5/go.mod h1:+Aoyx4M0etg6GikzCrip1VtvAtUlMlo2Aq+GHwQSqOA=
159156
modernc.org/libc v1.74.0 h1:rkouuwU6Yqp7ZhwytqBhAiulUi/wvH2KsizSt9R4Hh0=
160157
modernc.org/libc v1.74.0/go.mod h1:uH4t5bOx3G3g9Xcmj10YKlTcVISlRDwv8VoQJG9n8Os=
158+
modernc.org/libc v1.74.1 h1:bdR4VTKFMC4966QSNZ05XLGI/VwzVa2kTUX51Dm0riQ=
159+
modernc.org/libc v1.74.1/go.mod h1:uH4t5bOx3G3g9Xcmj10YKlTcVISlRDwv8VoQJG9n8Os=
161160
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
162161
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
163162
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=

0 commit comments

Comments
 (0)