add word wrapping for lines which are longer than the terminal width (#553)
This commit is contained in:
parent
e1a0846483
commit
c928ceb927
2 changed files with 65 additions and 25 deletions
89
cmd/cmd.go
89
cmd/cmd.go
|
@ -18,11 +18,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pdevine/readline"
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
|
"github.com/pdevine/readline"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/term"
|
||||||
|
|
||||||
"github.com/jmorganca/ollama/api"
|
"github.com/jmorganca/ollama/api"
|
||||||
"github.com/jmorganca/ollama/format"
|
"github.com/jmorganca/ollama/format"
|
||||||
|
@ -400,6 +401,29 @@ func generate(cmd *cobra.Command, model, prompt string) error {
|
||||||
generateContext = []int{}
|
generateContext = []int{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wrapTerm bool
|
||||||
|
termType := os.Getenv("TERM")
|
||||||
|
if termType == "xterm-256color" {
|
||||||
|
wrapTerm = true
|
||||||
|
}
|
||||||
|
|
||||||
|
termWidth, _, err := term.GetSize(int(0))
|
||||||
|
if err != nil {
|
||||||
|
wrapTerm = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// override wrapping if the user turned it off
|
||||||
|
nowrap, err := cmd.Flags().GetBool("nowordwrap")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if nowrap {
|
||||||
|
wrapTerm = false
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentLineLength int
|
||||||
|
var wordBuffer string
|
||||||
|
|
||||||
request := api.GenerateRequest{Model: model, Prompt: prompt, Context: generateContext}
|
request := api.GenerateRequest{Model: model, Prompt: prompt, Context: generateContext}
|
||||||
fn := func(response api.GenerateResponse) error {
|
fn := func(response api.GenerateResponse) error {
|
||||||
if !spinner.IsFinished() {
|
if !spinner.IsFinished() {
|
||||||
|
@ -408,7 +432,31 @@ func generate(cmd *cobra.Command, model, prompt string) error {
|
||||||
|
|
||||||
latest = response
|
latest = response
|
||||||
|
|
||||||
fmt.Print(response.Response)
|
if wrapTerm {
|
||||||
|
for _, ch := range response.Response {
|
||||||
|
if currentLineLength+1 > termWidth-5 {
|
||||||
|
// backtrack the length of the last word and clear to the end of the line
|
||||||
|
fmt.Printf("\x1b[%dD\x1b[K\n", len(wordBuffer))
|
||||||
|
fmt.Printf("%s%c", wordBuffer, ch)
|
||||||
|
currentLineLength = len(wordBuffer) + 1
|
||||||
|
} else {
|
||||||
|
fmt.Print(string(ch))
|
||||||
|
currentLineLength += 1
|
||||||
|
|
||||||
|
switch ch {
|
||||||
|
case ' ':
|
||||||
|
wordBuffer = ""
|
||||||
|
case '\n':
|
||||||
|
currentLineLength = 0
|
||||||
|
default:
|
||||||
|
wordBuffer += string(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Print(response.Response)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,7 +475,6 @@ func generate(cmd *cobra.Command, model, prompt string) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if prompt != "" {
|
if prompt != "" {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
@ -470,13 +517,10 @@ func generateInteractive(cmd *cobra.Command, model string) error {
|
||||||
readline.PcItem("/set",
|
readline.PcItem("/set",
|
||||||
readline.PcItem("history"),
|
readline.PcItem("history"),
|
||||||
readline.PcItem("nohistory"),
|
readline.PcItem("nohistory"),
|
||||||
|
readline.PcItem("wordwrap"),
|
||||||
|
readline.PcItem("nowordwrap"),
|
||||||
readline.PcItem("verbose"),
|
readline.PcItem("verbose"),
|
||||||
readline.PcItem("quiet"),
|
readline.PcItem("quiet"),
|
||||||
readline.PcItem("mode",
|
|
||||||
readline.PcItem("vim"),
|
|
||||||
readline.PcItem("emacs"),
|
|
||||||
readline.PcItem("default"),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
readline.PcItem("/show",
|
readline.PcItem("/show",
|
||||||
readline.PcItem("license"),
|
readline.PcItem("license"),
|
||||||
|
@ -535,6 +579,7 @@ func generateInteractive(cmd *cobra.Command, model string) error {
|
||||||
line = multiLineBuffer
|
line = multiLineBuffer
|
||||||
multiLineBuffer = ""
|
multiLineBuffer = ""
|
||||||
scanner.SetPrompt(">>> ")
|
scanner.SetPrompt(">>> ")
|
||||||
|
continue
|
||||||
} else {
|
} else {
|
||||||
multiLineBuffer += line + " "
|
multiLineBuffer += line + " "
|
||||||
continue
|
continue
|
||||||
|
@ -549,45 +594,42 @@ func generateInteractive(cmd *cobra.Command, model string) error {
|
||||||
if err := ListHandler(cmd, args[1:]); err != nil {
|
if err := ListHandler(cmd, args[1:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
|
||||||
case strings.HasPrefix(line, "/set"):
|
case strings.HasPrefix(line, "/set"):
|
||||||
args := strings.Fields(line)
|
args := strings.Fields(line)
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
switch args[1] {
|
switch args[1] {
|
||||||
case "history":
|
case "history":
|
||||||
scanner.HistoryEnable()
|
scanner.HistoryEnable()
|
||||||
continue
|
|
||||||
case "nohistory":
|
case "nohistory":
|
||||||
scanner.HistoryDisable()
|
scanner.HistoryDisable()
|
||||||
continue
|
case "wordwrap":
|
||||||
|
cmd.Flags().Set("nowordwrap", "false")
|
||||||
|
fmt.Println("Set 'wordwrap' mode.")
|
||||||
|
case "nowordwrap":
|
||||||
|
cmd.Flags().Set("nowordwrap", "true")
|
||||||
|
fmt.Println("Set 'nowordwrap' mode.")
|
||||||
case "verbose":
|
case "verbose":
|
||||||
cmd.Flags().Set("verbose", "true")
|
cmd.Flags().Set("verbose", "true")
|
||||||
continue
|
fmt.Println("Set 'verbose' mode.")
|
||||||
case "quiet":
|
case "quiet":
|
||||||
cmd.Flags().Set("verbose", "false")
|
cmd.Flags().Set("verbose", "false")
|
||||||
continue
|
fmt.Println("Set 'quiet' mode.")
|
||||||
case "mode":
|
case "mode":
|
||||||
if len(args) > 2 {
|
if len(args) > 2 {
|
||||||
switch args[2] {
|
switch args[2] {
|
||||||
case "vim":
|
case "vim":
|
||||||
scanner.SetVimMode(true)
|
scanner.SetVimMode(true)
|
||||||
continue
|
|
||||||
case "emacs", "default":
|
case "emacs", "default":
|
||||||
scanner.SetVimMode(false)
|
scanner.SetVimMode(false)
|
||||||
continue
|
|
||||||
default:
|
default:
|
||||||
usage()
|
usage()
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
usage()
|
usage()
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
usage()
|
usage()
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
case strings.HasPrefix(line, "/show"):
|
case strings.HasPrefix(line, "/show"):
|
||||||
args := strings.Fields(line)
|
args := strings.Fields(line)
|
||||||
|
@ -595,7 +637,6 @@ func generateInteractive(cmd *cobra.Command, model string) error {
|
||||||
resp, err := server.GetModelInfo(model)
|
resp, err := server.GetModelInfo(model)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("error: couldn't get model")
|
fmt.Println("error: couldn't get model")
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch args[1] {
|
switch args[1] {
|
||||||
|
@ -612,17 +653,16 @@ func generateInteractive(cmd *cobra.Command, model string) error {
|
||||||
default:
|
default:
|
||||||
fmt.Println("error: unknown command")
|
fmt.Println("error: unknown command")
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
|
||||||
} else {
|
} else {
|
||||||
usage()
|
usage()
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
case line == "/help", line == "/?":
|
case line == "/help", line == "/?":
|
||||||
usage()
|
usage()
|
||||||
continue
|
|
||||||
case line == "/exit", line == "/bye":
|
case line == "/exit", line == "/bye":
|
||||||
return nil
|
return nil
|
||||||
|
case strings.HasPrefix(line, "/"):
|
||||||
|
args := strings.Fields(line)
|
||||||
|
fmt.Printf("Unknown command '%s'. Type /? for help\n", args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(line) > 0 && line[0] != '/' {
|
if len(line) > 0 && line[0] != '/' {
|
||||||
|
@ -828,6 +868,7 @@ func NewCLI() *cobra.Command {
|
||||||
|
|
||||||
runCmd.Flags().Bool("verbose", false, "Show timings for response")
|
runCmd.Flags().Bool("verbose", false, "Show timings for response")
|
||||||
runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
|
runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
|
||||||
|
runCmd.Flags().Bool("nowordwrap", false, "Don't wrap words to the next line automatically")
|
||||||
|
|
||||||
serveCmd := &cobra.Command{
|
serveCmd := &cobra.Command{
|
||||||
Use: "serve",
|
Use: "serve",
|
||||||
|
|
1
go.sum
1
go.sum
|
@ -120,7 +120,6 @@ golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
|
Loading…
Reference in a new issue