From 4a5a6e673fdf735f8b407c122e2562204e269015 Mon Sep 17 00:00:00 2001
From: reugn
Date: Tue, 5 Mar 2024 09:58:32 +0200
Subject: [PATCH] feat: add a system command to toggle input mode
---
README.md | 9 ++++----
cli/chat.go | 7 ++++--
cli/command.go | 58 ++++++++++++++++++++++++++++----------------------
3 files changed, 42 insertions(+), 32 deletions(-)
diff --git a/README.md b/README.md
index 915d446..f91aa25 100644
--- a/README.md
+++ b/README.md
@@ -36,10 +36,11 @@ To use `gemini-cli`, you'll need an API key set in the `GEMINI_API_KEY` environm
The system chat message must begin with an exclamation mark and is used for internal operations.
A short list of supported system commands:
-| Command | Descripton |
-| --- | --- |
-| !q | Quit the application |
-| !p | Purge the chat history |
+| Command | Descripton
+| --- | ---
+| !q | Quit the application
+| !p | Delete the history used as chat context by the model
+| !m | Toggle input mode (single-line <-> multi-line)
### CLI help
```console
diff --git a/cli/chat.go b/cli/chat.go
index 1a0e0c1..f151934 100644
--- a/cli/chat.go
+++ b/cli/chat.go
@@ -33,6 +33,9 @@ func NewChat(user string, model *gemini.ChatSession, opts *ChatOpts) (*Chat, err
}
prompt := newPrompt(user)
reader.SetPrompt(prompt.user)
+ if opts.Multiline {
+ reader.HistoryDisable()
+ }
return &Chat{
model: model,
prompt: prompt,
@@ -93,9 +96,9 @@ func (c *Chat) readMultiLine() (string, bool) {
func (c *Chat) parseCommand(message string) command {
if strings.HasPrefix(message, systemCmdPrefix) {
- return newSystemCommand(c.model, c.prompt)
+ return newSystemCommand(c)
}
- return newGeminiCommand(c.model, c.prompt, c.opts)
+ return newGeminiCommand(c)
}
func (c *Chat) handleReadError(err error) (string, bool) {
diff --git a/cli/command.go b/cli/command.go
index 50d5e9b..1432a82 100644
--- a/cli/command.go
+++ b/cli/command.go
@@ -10,13 +10,14 @@ import (
"github.com/charmbracelet/glamour"
"github.com/reugn/gemini-cli/cli/color"
- "github.com/reugn/gemini-cli/gemini"
"google.golang.org/api/iterator"
)
const (
- systemCmdPrefix = "!"
- systemCmdQuit = "!q"
+ systemCmdPrefix = "!"
+ systemCmdQuit = "!q"
+ systemCmdPurgeHistory = "!p"
+ systemCmdToggleInputMode = "!m"
)
type command interface {
@@ -24,28 +25,37 @@ type command interface {
}
type systemCommand struct {
- model *gemini.ChatSession
- prompt *prompt
+ chat *Chat
}
var _ command = (*systemCommand)(nil)
-func newSystemCommand(model *gemini.ChatSession, prompt *prompt) command {
+func newSystemCommand(chat *Chat) command {
return &systemCommand{
- model: model,
- prompt: prompt,
+ chat: chat,
}
}
func (c *systemCommand) run(message string) bool {
- message = strings.TrimPrefix(message, systemCmdPrefix)
switch message {
- case "q":
+ case systemCmdQuit:
c.print("Exiting gemini-cli...")
return true
- case "p":
- c.model.ClearHistory()
+ case systemCmdPurgeHistory:
+ c.chat.model.ClearHistory()
c.print("Cleared the chat history.")
+ case systemCmdToggleInputMode:
+ if c.chat.opts.Multiline {
+ c.print("Switched to single-line input mode.")
+ c.chat.reader.HistoryEnable()
+ c.chat.opts.Multiline = false
+ } else {
+ c.print("Switched to multi-line input mode.")
+ // disable history for multi-line messages since it is
+ // unusable for future requests
+ c.chat.reader.HistoryDisable()
+ c.chat.opts.Multiline = true
+ }
default:
c.print("Unknown system command.")
}
@@ -53,35 +63,31 @@ func (c *systemCommand) run(message string) bool {
}
func (c *systemCommand) print(message string) {
- fmt.Printf("%s%s\n", c.prompt.cli, message)
+ fmt.Printf("%s%s\n", c.chat.prompt.cli, message)
}
type geminiCommand struct {
- model *gemini.ChatSession
- prompt *prompt
+ chat *Chat
spinner *spinner
writer *bufio.Writer
- opts *ChatOpts
}
var _ command = (*geminiCommand)(nil)
-func newGeminiCommand(model *gemini.ChatSession, prompt *prompt, opts *ChatOpts) command {
+func newGeminiCommand(chat *Chat) command {
writer := bufio.NewWriter(os.Stdout)
return &geminiCommand{
- model: model,
- prompt: prompt,
+ chat: chat,
spinner: newSpinner(5, time.Second, writer),
writer: writer,
- opts: opts,
}
}
func (c *geminiCommand) run(message string) bool {
- c.printFlush(c.prompt.gemini)
+ c.printFlush(c.chat.prompt.gemini)
c.spinner.start()
- if c.opts.Format {
- // requires full markdown for formatting
+ if c.chat.opts.Format {
+ // requires the entire response to be formatted
c.runBlocking(message)
} else {
c.runStreaming(message)
@@ -90,7 +96,7 @@ func (c *geminiCommand) run(message string) bool {
}
func (c *geminiCommand) runBlocking(message string) {
- response, err := c.model.SendMessage(message)
+ response, err := c.chat.model.SendMessage(message)
c.spinner.stop()
if err != nil {
fmt.Println(color.Red(err.Error()))
@@ -101,7 +107,7 @@ func (c *geminiCommand) runBlocking(message string) {
buf.WriteString(fmt.Sprintf("%s", part))
}
}
- output, err := glamour.Render(buf.String(), c.opts.Style)
+ output, err := glamour.Render(buf.String(), c.chat.opts.Style)
if err != nil {
fmt.Printf(color.Red("Failed to format: %s\n"), err)
fmt.Println(buf.String())
@@ -112,7 +118,7 @@ func (c *geminiCommand) runBlocking(message string) {
}
func (c *geminiCommand) runStreaming(message string) {
- responseIterator := c.model.SendMessageStream(message)
+ responseIterator := c.chat.model.SendMessageStream(message)
c.spinner.stop()
for {
response, err := responseIterator.Next()