From afb3c6e184dbe75d85ea21e217a6e27dc0656946 Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sat, 2 Sep 2023 22:20:20 +0900 Subject: [PATCH 01/10] feat: add interactive UI for commit message suggestion --- cmd/suggest.go | 87 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/cmd/suggest.go b/cmd/suggest.go index 5f96611..8985d23 100644 --- a/cmd/suggest.go +++ b/cmd/suggest.go @@ -4,37 +4,92 @@ import ( "context" "fmt" "log" - "os/exec" + "strings" + tea "github.com/charmbracelet/bubbletea" "github.com/cocoide/commitify/internal/gateway" + "github.com/cocoide/commitify/internal/service" "github.com/cocoide/commitify/util" + "github.com/fatih/color" "github.com/spf13/cobra" ) -const ( - CommitMessagePrompt = "Generate commit message for [%s]" - FormatNotice = ", format your commit as:\n- feat: [feature description]\n- bugfix: [bugfix description]" -) +type model struct { + choices []string + currentIdx int + errorMsg string +} + +func (m model) Init() tea.Cmd { + return nil +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.Type { + case tea.KeyUp: + if m.currentIdx > 0 { + m.currentIdx-- + } + case tea.KeyDown: + if m.currentIdx < len(m.choices)-1 { + m.currentIdx++ + } + case tea.KeyEnter: + if err := util.ExecCommitMessage(m.choices[m.currentIdx]); err != nil { + m.errorMsg = err.Error() + return m, nil + } + return m, tea.Quit + case tea.KeyCtrlC, tea.KeyEsc: + return m, tea.Quit + } + } + return m, nil +} + +func (m model) View() string { + var b strings.Builder + if m.errorMsg != "" { + // エラーメッセージを赤色で表示 + red := color.New(color.FgRed).SprintFunc() + b.WriteString(red(m.errorMsg) + "\n\n") + } + b.WriteString("Please select an option:\n\n") + + for i, choice := range m.choices { + blue := color.New(color.FgCyan).SprintFunc() + if i == m.currentIdx { + b.WriteString(fmt.Sprintf(blue("-> %s\n"), choice)) + } else { + b.WriteString(fmt.Sprintf(" %s\n", choice)) + } + } + + b.WriteString("\nUse the arrow keys to navigate and press Enter to select.") + return b.String() +} var suggestCmd = &cobra.Command{ - Use: "suggest", - Short: "Suggestion of commit message for staging repository", + Use: "suggest", + Short: "Suggestion of commit message for staging repository", + Aliases: []string{"s", "suggest"}, Run: func(cmd *cobra.Command, args []string) { util.LoadEnv() ctx := context.Background() og := gateway.NewOpenAIGateway(ctx) - result, err := exec.Command("git", "diff", "--staged").Output() - if err != nil { - log.Fatal(err.Error()) - } - // 設定に応じてPromptは動的に変化させる - prompt := fmt.Sprintf(CommitMessagePrompt, string(result)) - answer, err := og.GetAnswerFromPrompt(prompt, 0.01) - // 生成中のUIを非同期で表示 + ms := service.NewMessageService(og) + msgCh, err := ms.AsyncGenerateCommitMessage() if err != nil { log.Fatal(err.Error()) } - fmt.Println(answer) + suggestMsg := <-msgCh + fmt.Println(suggestMsg) + choices := []string{"32", "!", "!#!"} + m := model{choices: choices} + p := tea.NewProgram(m) + p.Run() }, } From 108f7e64bf66a1ef5daebfbb47744fa198070a5f Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sat, 2 Sep 2023 22:22:05 +0900 Subject: [PATCH 02/10] Fix commit error message and add suggested commit message options --- cmd/suggest.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/suggest.go b/cmd/suggest.go index 8985d23..eee7c16 100644 --- a/cmd/suggest.go +++ b/cmd/suggest.go @@ -38,8 +38,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case tea.KeyEnter: if err := util.ExecCommitMessage(m.choices[m.currentIdx]); err != nil { - m.errorMsg = err.Error() - return m, nil + m.errorMsg = "コミットエラーが発生" + return m, tea.Quit } return m, tea.Quit case tea.KeyCtrlC, tea.KeyEsc: @@ -52,7 +52,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) View() string { var b strings.Builder if m.errorMsg != "" { - // エラーメッセージを赤色で表示 red := color.New(color.FgRed).SprintFunc() b.WriteString(red(m.errorMsg) + "\n\n") } @@ -86,7 +85,7 @@ var suggestCmd = &cobra.Command{ } suggestMsg := <-msgCh fmt.Println(suggestMsg) - choices := []string{"32", "!", "!#!"} + choices := []string{suggestMsg, "!", "!#!"} m := model{choices: choices} p := tea.NewProgram(m) p.Run() From 7ae38eee1d9952e1fa3a827403a902120e40a702 Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sat, 2 Sep 2023 22:38:38 +0900 Subject: [PATCH 03/10] - feat: Added message service implementation --- internal/service/message.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 internal/service/message.go diff --git a/internal/service/message.go b/internal/service/message.go new file mode 100644 index 0000000..3f07801 --- /dev/null +++ b/internal/service/message.go @@ -0,0 +1,37 @@ +package service + +import ( + "fmt" + + "github.com/cocoide/commitify/internal/gateway" + "github.com/cocoide/commitify/util" +) + +const ( + CommitMessagePrompt = "Generate 3 commit messages for [%s], each message separated by a comma and a space" + FormatNotice = ", format commit as:\n- feat: [feature description]\n- bugfix: [bugfix description]" +) + +// メッセージの生成、加工に関するクラス +type MessageService interface { + AsyncGenerateCommitMessage() (<-chan string, error) +} + +type messageService struct { + og gateway.OpenAIGateway +} + +func NewMessageService(og gateway.OpenAIGateway) MessageService { + return &messageService{og: og} +} + +func (s *messageService) AsyncGenerateCommitMessage() (<-chan string, error) { + var result <-chan string + stagingCode := util.ExecGetStagingCode() + if len(stagingCode) < 1 { + return nil, fmt.Errorf("There is no staging code") + } + prompt := fmt.Sprintf(CommitMessagePrompt, string(stagingCode)) + result = s.og.AsyncGetAnswerFromPrompt(prompt, 0.01) + return result, nil +} From fae8178168c5dfaccfa07befe24f9c9e46808f6b Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sat, 2 Sep 2023 22:41:04 +0900 Subject: [PATCH 04/10] - Update suggestCmd to generate multiple commit messages asynchronously --- cmd/suggest.go | 13 +++++++------ internal/service/message.go | 10 ++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cmd/suggest.go b/cmd/suggest.go index eee7c16..a90b02d 100644 --- a/cmd/suggest.go +++ b/cmd/suggest.go @@ -65,8 +65,8 @@ func (m model) View() string { b.WriteString(fmt.Sprintf(" %s\n", choice)) } } - - b.WriteString("\nUse the arrow keys to navigate and press Enter to select.") + green := color.New(color.FgGreen).SprintFunc() + b.WriteString(green("\nUse the arrow keys to navigate and press Enter to select.")) return b.String() } @@ -79,13 +79,14 @@ var suggestCmd = &cobra.Command{ ctx := context.Background() og := gateway.NewOpenAIGateway(ctx) ms := service.NewMessageService(og) - msgCh, err := ms.AsyncGenerateCommitMessage() + messages, err := ms.AsyncGenerateCommitMessage() if err != nil { log.Fatal(err.Error()) } - suggestMsg := <-msgCh - fmt.Println(suggestMsg) - choices := []string{suggestMsg, "!", "!#!"} + var choices []string + for _, v := range messages { + choices = append(choices, v) + } m := model{choices: choices} p := tea.NewProgram(m) p.Run() diff --git a/internal/service/message.go b/internal/service/message.go index 3f07801..0e308a9 100644 --- a/internal/service/message.go +++ b/internal/service/message.go @@ -2,19 +2,20 @@ package service import ( "fmt" + "strings" "github.com/cocoide/commitify/internal/gateway" "github.com/cocoide/commitify/util" ) const ( - CommitMessagePrompt = "Generate 3 commit messages for [%s], each message separated by a comma and a space" + CommitMessagePrompt = "Generate commit messages for [%s]. Each message should be separated by only space" FormatNotice = ", format commit as:\n- feat: [feature description]\n- bugfix: [bugfix description]" ) // メッセージの生成、加工に関するクラス type MessageService interface { - AsyncGenerateCommitMessage() (<-chan string, error) + AsyncGenerateCommitMessage() ([]string, error) } type messageService struct { @@ -25,7 +26,7 @@ func NewMessageService(og gateway.OpenAIGateway) MessageService { return &messageService{og: og} } -func (s *messageService) AsyncGenerateCommitMessage() (<-chan string, error) { +func (s *messageService) AsyncGenerateCommitMessage() ([]string, error) { var result <-chan string stagingCode := util.ExecGetStagingCode() if len(stagingCode) < 1 { @@ -33,5 +34,6 @@ func (s *messageService) AsyncGenerateCommitMessage() (<-chan string, error) { } prompt := fmt.Sprintf(CommitMessagePrompt, string(stagingCode)) result = s.og.AsyncGetAnswerFromPrompt(prompt, 0.01) - return result, nil + messages := strings.Split(<-result, "\n") + return messages, nil } From 0e1fbf660b692903647ae6699a04a54677be21fd Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sat, 2 Sep 2023 22:45:27 +0900 Subject: [PATCH 05/10] Add util/exec.go file --- util/exec.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 util/exec.go diff --git a/util/exec.go b/util/exec.go new file mode 100644 index 0000000..5d5fba3 --- /dev/null +++ b/util/exec.go @@ -0,0 +1,24 @@ +package util + +import ( + "fmt" + "log" + "os/exec" +) + +func ExecGetStagingCode() string { + code, err := exec.Command("git", "diff", "--staged").Output() + if err != nil { + fmt.Printf("Gitでエラーが発生") + log.Fatal(err.Error()) + } + return string(code) +} + +func ExecCommitMessage(msg string) error { + cmd := exec.Command("git", "commit", "-m", msg) + if err := cmd.Run(); err != nil { + return err + } + return nil +} From 6d901b023b1e3c0b16441da143574794463af57e Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sat, 2 Sep 2023 22:45:46 +0900 Subject: [PATCH 06/10] Update module --- go.mod | 20 +++++++++++++++++++- go.sum | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3b8e5d1..fd7216c 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,30 @@ module github.com/cocoide/commitify go 1.19 require ( + github.com/charmbracelet/bubbletea v0.24.2 + github.com/fatih/color v1.15.0 + github.com/joho/godotenv v1.5.1 github.com/sashabaranov/go-openai v1.15.1 github.com/spf13/cobra v1.7.0 ) require ( + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/joho/godotenv v1.5.1 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.15.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.3.8 // indirect ) diff --git a/go.sum b/go.sum index 34fa8eb..caebe06 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,39 @@ +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= +github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs= +github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sashabaranov/go-openai v1.15.1 h1:BAV5LCVEzvZ3rN/Lh5NRVs2z6AahPt/jn5s2/cEEG0M= github.com/sashabaranov/go-openai v1.15.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= @@ -10,5 +41,15 @@ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 0005933302485fe1e312ee401fdfb981271b90f2 Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sat, 2 Sep 2023 23:31:09 +0900 Subject: [PATCH 07/10] Refactor loadenv.go file --- util/loadenv.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/loadenv.go b/util/loadenv.go index 95987e4..ccc7892 100644 --- a/util/loadenv.go +++ b/util/loadenv.go @@ -6,11 +6,12 @@ import ( "github.com/joho/godotenv" ) +// Viperを導入したらgodotenvのコードは削除する func LoadEnv() { err := godotenv.Load(".env") if err != nil { log.Printf("failed to load .env file: %v" + err.Error()) } else { - log.Print("success to load .env file") + // log.Print("success to load .env file") } } From 7d380f824de5a09c1b6e8eaf08f7620b794b67d4 Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sun, 3 Sep 2023 00:03:12 +0900 Subject: [PATCH 08/10] Update View() function in suggest.go --- cmd/suggest.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd/suggest.go b/cmd/suggest.go index a90b02d..5cf8dfe 100644 --- a/cmd/suggest.go +++ b/cmd/suggest.go @@ -55,18 +55,19 @@ func (m model) View() string { red := color.New(color.FgRed).SprintFunc() b.WriteString(red(m.errorMsg) + "\n\n") } - b.WriteString("Please select an option:\n\n") + white := color.New(color.FgWhite).SprintFunc() + b.WriteString(white("Please select an option:")) + b.WriteString(white("\n Use arrow ↑↓ to navigate and press Enter to select.\n\n")) for i, choice := range m.choices { - blue := color.New(color.FgCyan).SprintFunc() + cyan := color.New(color.FgCyan).SprintFunc() + hiCyan := color.New(color.FgHiCyan).SprintFunc() if i == m.currentIdx { - b.WriteString(fmt.Sprintf(blue("-> %s\n"), choice)) + b.WriteString(fmt.Sprintf(hiCyan("➡️ %s\n"), choice)) } else { - b.WriteString(fmt.Sprintf(" %s\n", choice)) + b.WriteString(fmt.Sprintf(cyan(" %s\n"), choice)) } } - green := color.New(color.FgGreen).SprintFunc() - b.WriteString(green("\nUse the arrow keys to navigate and press Enter to select.")) return b.String() } From e6741747c89341ea5172d22b54f7431be72d62c8 Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sun, 3 Sep 2023 01:15:00 +0900 Subject: [PATCH 09/10] 4. Update Update() function to handle generateMessages message and update model accordingly --- cmd/suggest.go | 54 +++++++++++++++++++++++++------------ internal/service/message.go | 2 +- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/cmd/suggest.go b/cmd/suggest.go index 5cf8dfe..21137d2 100644 --- a/cmd/suggest.go +++ b/cmd/suggest.go @@ -3,7 +3,6 @@ package cmd import ( "context" "fmt" - "log" "strings" tea "github.com/charmbracelet/bubbletea" @@ -18,14 +17,40 @@ type model struct { choices []string currentIdx int errorMsg string + isLoading bool + messages []string +} + +type generateMessages struct { + messages []string + errorMsg string } func (m model) Init() tea.Cmd { - return nil + return func() tea.Msg { + util.LoadEnv() + ctx := context.Background() + og := gateway.NewOpenAIGateway(ctx) + ms := service.NewMessageService(og) + messages, err := ms.AsyncGenerateCommitMessage() + if err != nil { + return generateMessages{errorMsg: "メッセージの生成に失敗: " + err.Error()} + } + return generateMessages{messages: messages} + } } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { + case generateMessages: + if msg.errorMsg != "" { + m.errorMsg = msg.errorMsg + m.isLoading = false + return m, nil + } + m.choices = msg.messages + m.isLoading = false + return m, nil case tea.KeyMsg: switch msg.Type { case tea.KeyUp: @@ -38,7 +63,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case tea.KeyEnter: if err := util.ExecCommitMessage(m.choices[m.currentIdx]); err != nil { - m.errorMsg = "コミットエラーが発生" + m.errorMsg = "コミットに失敗: " + err.Error() return m, tea.Quit } return m, tea.Quit @@ -50,13 +75,20 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m model) View() string { + if m.errorMsg != "" { + red := color.New(color.FgRed).SprintFunc() + return fmt.Sprintf(red(m.errorMsg)) + } + if m.isLoading { + return "🌎 Generating commit messages ..." + } var b strings.Builder if m.errorMsg != "" { red := color.New(color.FgRed).SprintFunc() b.WriteString(red(m.errorMsg) + "\n\n") } white := color.New(color.FgWhite).SprintFunc() - b.WriteString(white("Please select an option:")) + b.WriteString(white("🍕Please select an option:")) b.WriteString(white("\n Use arrow ↑↓ to navigate and press Enter to select.\n\n")) for i, choice := range m.choices { @@ -76,19 +108,7 @@ var suggestCmd = &cobra.Command{ Short: "Suggestion of commit message for staging repository", Aliases: []string{"s", "suggest"}, Run: func(cmd *cobra.Command, args []string) { - util.LoadEnv() - ctx := context.Background() - og := gateway.NewOpenAIGateway(ctx) - ms := service.NewMessageService(og) - messages, err := ms.AsyncGenerateCommitMessage() - if err != nil { - log.Fatal(err.Error()) - } - var choices []string - for _, v := range messages { - choices = append(choices, v) - } - m := model{choices: choices} + m := model{isLoading: true} p := tea.NewProgram(m) p.Run() }, diff --git a/internal/service/message.go b/internal/service/message.go index 0e308a9..b2054d5 100644 --- a/internal/service/message.go +++ b/internal/service/message.go @@ -9,7 +9,7 @@ import ( ) const ( - CommitMessagePrompt = "Generate commit messages for [%s]. Each message should be separated by only space" + CommitMessagePrompt = "Generate up to 5 commit messages for [%s]. Each message should be separated by only space" FormatNotice = ", format commit as:\n- feat: [feature description]\n- bugfix: [bugfix description]" ) From 21de4580d0c463b5d36119dda3f19e40a2f773f5 Mon Sep 17 00:00:00 2001 From: {cocoide} Date: Sun, 3 Sep 2023 01:15:50 +0900 Subject: [PATCH 10/10] Fixed service method name --- cmd/suggest.go | 2 +- internal/service/message.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/suggest.go b/cmd/suggest.go index 21137d2..1ba365b 100644 --- a/cmd/suggest.go +++ b/cmd/suggest.go @@ -32,7 +32,7 @@ func (m model) Init() tea.Cmd { ctx := context.Background() og := gateway.NewOpenAIGateway(ctx) ms := service.NewMessageService(og) - messages, err := ms.AsyncGenerateCommitMessage() + messages, err := ms.GenerateCommitMessage() if err != nil { return generateMessages{errorMsg: "メッセージの生成に失敗: " + err.Error()} } diff --git a/internal/service/message.go b/internal/service/message.go index b2054d5..80a9b8d 100644 --- a/internal/service/message.go +++ b/internal/service/message.go @@ -15,7 +15,7 @@ const ( // メッセージの生成、加工に関するクラス type MessageService interface { - AsyncGenerateCommitMessage() ([]string, error) + GenerateCommitMessage() ([]string, error) } type messageService struct { @@ -26,7 +26,7 @@ func NewMessageService(og gateway.OpenAIGateway) MessageService { return &messageService{og: og} } -func (s *messageService) AsyncGenerateCommitMessage() ([]string, error) { +func (s *messageService) GenerateCommitMessage() ([]string, error) { var result <-chan string stagingCode := util.ExecGetStagingCode() if len(stagingCode) < 1 {