Compare commits

..

2 Commits

Author SHA1 Message Date
9d83013f52 tui: use ctx chroma highlighter 2024-03-12 08:36:33 +00:00
e92a0ff50d Add initial TUI 2024-03-12 08:36:33 +00:00
2 changed files with 1 additions and 28 deletions

View File

@ -1,18 +0,0 @@
package tui
var PlaceholderMessages []message = []message{
// placeholder messages
{role: "Assistant", content: "Hello! I'm here to help you with any programming questions you might have. What language are you working with today?"},
{role: "User", content: "I'm currently learning Go and I'm having trouble understanding goroutines. Can you explain them to me?"},
{role: "Assistant", content: "Of course! Goroutines are lightweight threads managed by the Go runtime. They allow you to write concurrent programs more easily. Here's a simple example of how to start a goroutine:\n\n```go\nfunc main() {\n\tgo sayHello(\"Alice\")\n\tgo sayHello(\"Bob\")\n\ttime.Sleep(time.Second)\n\tfmt.Println(\"main function\")\n}\n\nfunc sayHello(name string) {\n\tfmt.Printf(\"Hello, %s!\\n\", name)\n}\n```\n\nIn this example, the `sayHello` function is called as a goroutine using the `go` keyword. The main function continues execution immediately after starting the goroutines, and the `time.Sleep` call is used to prevent the program from exiting before the goroutines have a chance to run."},
{role: "User", content: "Thanks for the explanation! I think I understand the basics now. Are there any best practices or common pitfalls I should be aware of when using goroutines?"},
{role: "Assistant", content: "Yes, there are a few best practices and common pitfalls to keep in mind when working with goroutines:\n\n1. **Synchronization**: When multiple goroutines access shared resources, it's important to use proper synchronization mechanisms like mutexes or channels to avoid race conditions.\n\n2. **Goroutine Leaks**: Make sure to properly terminate goroutines when they are no longer needed to prevent goroutine leaks, which can lead to resource exhaustion.\n\n3. **Deadlocks**: Be cautious of situations where goroutines are waiting for each other indefinitely, causing a deadlock. This can happen when there's a circular dependency between goroutines.\n\n4. **Error Handling**: Goroutines should handle errors gracefully and communicate them back to the main goroutine if necessary. Unhandled errors in goroutines can cause unexpected behavior.\n\n5. **Bounded Concurrency**: Avoid creating an unbounded number of goroutines, as it can lead to resource exhaustion. Use techniques like worker pools or semaphores to limit the number of concurrent goroutines.\n\nHere's an example of using a `WaitGroup` to wait for multiple goroutines to finish:\n\n```go\nfunc main() {\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t// Goroutine 1 work\n\t}()\n\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t// Goroutine 2 work\n\t}()\n\n\twg.Wait()\n\tfmt.Println(\"All goroutines finished\")\n}\n```\n\nBy following these best practices and being mindful of potential pitfalls, you can write safe and efficient concurrent programs using goroutines."},
{role: "User", content: "What are some common use cases for goroutines in real-world applications?"},
{role: "Assistant", content: "Goroutines are commonly used in several real-world scenarios, such as:\n\n1. **Handling multiple client requests**: In web servers or API backends, goroutines can be used to handle incoming client requests concurrently, improving the server's responsiveness and throughput.\n\n2. **Parallel processing**: Goroutines can be used to parallelize computationally intensive tasks, distributing the workload across multiple CPU cores to improve performance.\n\n3. **Background tasks**: Goroutines are useful for running background tasks asynchronously, such as sending emails, generating reports, or performing periodic cleanup operations, without blocking the main execution flow.\n\n4. **Event-driven systems**: In event-driven architectures, goroutines can be used to handle events concurrently, allowing the system to process multiple events simultaneously without blocking.\n\n5. **Pipelines and data processing**: Goroutines can be used to create pipelines where data flows through a series of stages, with each stage processing the data concurrently, enabling efficient data processing and transformation."},
{role: "User", content: "How can I gracefully stop a goroutine that's running in an infinite loop?"},
{role: "Assistant", content: "To gracefully stop a goroutine that's running in an infinite loop, you can use a `select` statement with a `done` channel. Here's an example:\n\n```go\nfunc worker(done <-chan bool) {\n\tfor {\n\t\tselect {\n\t\tcase <-done:\n\t\t\tfmt.Println(\"Goroutine stopped\")\n\t\t\treturn\n\t\tdefault:\n\t\t\t// Perform work\n\t\t\tfmt.Println(\"Working...\")\n\t\t\ttime.Sleep(time.Second)\n\t\t}\n\t}\n}\n\nfunc main() {\n\tdone := make(chan bool)\n\tgo worker(done)\n\n\t// Do some other work\n\ttime.Sleep(5 * time.Second)\n\n\t// Signal the goroutine to stop\n\tdone <- true\n\tfmt.Println(\"Main function finished\")\n}\n```\n\nIn this example, the `worker` goroutine runs in an infinite loop, but it also listens on the `done` channel using a `select` statement. When a value is sent on the `done` channel, the goroutine will receive it, print a message, and return, effectively stopping its execution.\n\nIn the `main` function, we create the `done` channel, start the `worker` goroutine, perform some other work, and then signal the goroutine to stop by sending a value on the `done` channel.\n\nBy using this pattern, you can gracefully stop a goroutine that's running in an infinite loop, allowing it to perform any necessary cleanup or termination tasks before exiting."},
{role: "User", content: "What are channels in Go and how do they help with communication between goroutines?"},
{role: "Assistant", content: "Channels in Go are a fundamental mechanism for communication and synchronization between goroutines. They provide a way for goroutines to send and receive values, allowing them to exchange data and coordinate their execution.\n\nHere are some key points about channels in Go:\n\n1. **Declaration**: Channels are declared using the `chan` keyword, specifying the type of values they can send and receive. For example, `ch := make(chan int)` creates a channel that can send and receive integers.\n\n2. **Sending and Receiving**: Goroutines can send values to a channel using the `<-` operator, like `ch <- value`, and receive values from a channel using the same operator, like `value := <-ch`. Sending and receiving operations block until the other side is ready, ensuring synchronization between goroutines.\n\n3. **Buffered Channels**: Channels can be created with a specified buffer size, like `ch := make(chan int, 10)`. Buffered channels allow sending goroutines to send multiple values without blocking until the buffer is full, and receiving goroutines can receive values without blocking until the buffer is empty.\n\n4. **Range and Close**: The `range` keyword can be used to iterate over values received from a channel until the channel is closed. Closing a channel indicates that no more values will be sent on it, and is done using the `close()` function.\n\nHere's an example that demonstrates communication between goroutines using channels:\n\n```go\nfunc worker(id int, jobs <-chan int, results chan<- int) {\n\tfor job := range jobs {\n\t\tfmt.Printf(\"Worker %d processing job %d\\n\", id, job)\n\t\tresults <- job * 2\n\t}\n}\n\nfunc main() {\n\tjobs := make(chan int, 10)\n\tresults := make(chan int, 10)\n\n\tfor i := 1; i <= 3; i++ {\n\t\tgo worker(i, jobs, results)\n\t}\n\n\tfor j := 1; j <= 5; j++ {\n\t\tjobs <- j\n\t}\n\tclose(jobs)\n\n\tfor r := 1; r <= 5; r++ {\n\t\tresult := <-results\n\t\tfmt.Printf(\"Result: %d\\n\", result)\n\t}\n}\n```\n\nIn this example, the `main` function creates two channels: `jobs` and `results`. It starts three worker goroutines, each receiving jobs from the `jobs` channel, processing them, and sending the results to the `results` channel. The `main` function sends jobs to the `jobs` channel, closes it, and then receives the processed results from the `results` channel.\n\nChannels provide a clean and efficient way for goroutines to communicate and synchronize their execution, making it easier to write concurrent programs in Go."},
{role: "User", content: "This is really helpful! I feel like I have a much better understanding of goroutines and channels now. Thank you for the detailed explanations and examples."},
{role: "Assistant", content: "You're welcome! I'm glad the explanations and examples helped you gain a better understanding of goroutines and channels in Go. Concurrent programming can be challenging at first, but with practice and a solid grasp of these concepts, you'll be able to write efficient and robust concurrent programs.\n\nRemember to keep the following points in mind when working with goroutines and channels:\n\n1. Use goroutines to perform concurrent tasks and achieve parallelism when appropriate.\n2. Utilize channels for communication and synchronization between goroutines.\n3. Be mindful of potential race conditions and use proper synchronization mechanisms when accessing shared resources.\n4. Use the `select` statement to handle multiple channel operations and enable graceful termination of goroutines.\n5. Consider using buffered channels when you need to decouple the sending and receiving goroutines and allow for some level of asynchrony.\n\nAs you continue to explore and work with Go's concurrency features, you'll encounter more advanced patterns and techniques, such as the `sync` package, `context` package, and more. Don't hesitate to dive deeper into these topics and experiment with different approaches to concurrent programming.\n\nIf you have any more questions or need further clarification on any aspect of goroutines, channels, or concurrency in Go, feel free to ask. I'm here to help you along your learning journey!"},
}

View File

@ -12,7 +12,6 @@ import (
"git.mlow.ca/mlow/lmcli/pkg/lmcli"
models "git.mlow.ca/mlow/lmcli/pkg/lmcli/model"
"git.mlow.ca/mlow/lmcli/pkg/lmcli/tools"
"git.mlow.ca/mlow/lmcli/pkg/util/tty"
"github.com/charmbracelet/bubbles/textarea"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
@ -23,8 +22,6 @@ type model struct {
ctx *lmcli.Context
convShortname string
highlighter *tty.ChromaHighlighter
// application state
conversation *models.Conversation
messages []models.Message
@ -154,12 +151,6 @@ func initialModel(ctx *lmcli.Context, convShortname string) model {
confirmPrompt: false,
}
m.highlighter = tty.NewChromaHighlighter(
"markdown", // we're highlighting markdown
*ctx.Config.Chroma.Formatter,
*ctx.Config.Chroma.Style,
)
m.content = viewport.New(0, 0)
m.input = textarea.New()
@ -294,7 +285,7 @@ func (m *model) updateContent() {
sb.WriteString(fmt.Sprintf("%s:\n\n", style.Render(string(message.Role))))
highlighted, _ := m.highlighter.HighlightS(message.Content)
highlighted, _ := m.ctx.Chroma.HighlightS(message.Content)
sb.WriteString(contentStyle.Width(m.content.Width - 5).Render(highlighted))
if i < msgCnt-1 {
sb.WriteString("\n\n")