diff --git a/go.mod b/go.mod index c171c6b..dbc0be7 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module git.mlow.ca/mlow/lmcli go 1.21 require ( + github.com/alecthomas/chroma/v2 v2.11.1 github.com/go-yaml/yaml v2.1.0+incompatible github.com/gookit/color v1.5.4 github.com/sashabaranov/go-openai v1.17.7 @@ -13,6 +14,7 @@ require ( ) require ( + github.com/dlclark/regexp2 v1.10.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect diff --git a/go.sum b/go.sum index b84a696..c9309f8 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,21 @@ +github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.11.1 h1:m9uUtgcdAwgfFNxuqj7AIG75jD2YmL61BBIJWtdzJPs= +github.com/alecthomas/chroma/v2 v2.11.1/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= diff --git a/pkg/cli/config.go b/pkg/cli/config.go index b9c7137..4774e4e 100644 --- a/pkg/cli/config.go +++ b/pkg/cli/config.go @@ -14,6 +14,10 @@ type Config struct { DefaultModel *string `yaml:"defaultModel" default:"gpt-4"` DefaultMaxLength *int `yaml:"defaultMaxLength" default:"256"` } `yaml:"openai"` + Chroma *struct { + Style *string `yaml:"style" default:"onedark"` + Formatter *string `yaml:"formatter" default:"terminal16m"` + } `yaml:"chroma"` } func getConfigDir() string { diff --git a/pkg/cli/tty.go b/pkg/cli/tty.go index bb00174..ad0ed9b 100644 --- a/pkg/cli/tty.go +++ b/pkg/cli/tty.go @@ -2,9 +2,11 @@ package cli import ( "fmt" + "os" "strings" "time" + "github.com/alecthomas/chroma/v2/quick" "github.com/gookit/color" ) @@ -60,9 +62,9 @@ func HandleDelayedResponse(response chan string) string { return sb.String() } -// RenderConversation renders the given messages, with optional space for a -// subsequent message. spaceForResponse controls how many newlines are printed -// after the final message (1 newline if false, 2 if true) +// RenderConversation renders the given messages to TTY, with optional space +// for a subsequent message. spaceForResponse controls how many '\n' characters +// are printed immediately after the final message (1 if false, 2 if true) func RenderConversation(messages []Message, spaceForResponse bool) { l := len(messages) for i, message := range messages { @@ -74,6 +76,12 @@ func RenderConversation(messages []Message, spaceForResponse bool) { } } +// HighlightMarkdown applies syntax highlighting to the provided markdown text +// and writes it to stdout. +func HighlightMarkdown(markdownText string) error { + return quick.Highlight(os.Stdout, markdownText, "md", *config.Chroma.Formatter, *config.Chroma.Style) +} + func (m *Message) RenderTTY() { var messageAge string if m.CreatedAt.IsZero() { @@ -103,6 +111,7 @@ func (m *Message) RenderTTY() { fmt.Printf("%s %s - %s %s\n\n", separator, role, timestamp, separator) if m.OriginalContent != "" { - fmt.Println(m.OriginalContent) + HighlightMarkdown(m.OriginalContent) + fmt.Println() } }