mirror of
https://github.com/aljazceru/opencode.git
synced 2026-01-04 00:14:59 +01:00
add initial lsp support
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/kujtimiihoxha/termai/internal/app"
|
||||
"github.com/kujtimiihoxha/termai/internal/llm/agent"
|
||||
"github.com/kujtimiihoxha/termai/internal/lsp/protocol"
|
||||
"github.com/kujtimiihoxha/termai/internal/message"
|
||||
"github.com/kujtimiihoxha/termai/internal/pubsub"
|
||||
"github.com/kujtimiihoxha/termai/internal/session"
|
||||
@@ -39,6 +41,7 @@ type messagesCmp struct {
|
||||
height int
|
||||
focused bool
|
||||
cachedView string
|
||||
timeLoaded time.Time
|
||||
}
|
||||
|
||||
func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
@@ -51,7 +54,8 @@ func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.viewport.GotoBottom()
|
||||
}
|
||||
for _, v := range m.messages {
|
||||
for _, c := range v.ToolCalls {
|
||||
for _, c := range v.ToolCalls() {
|
||||
// the message is being added to the session of a tool called
|
||||
if c.ID == msg.Payload.SessionID {
|
||||
m.renderView()
|
||||
m.viewport.GotoBottom()
|
||||
@@ -130,12 +134,11 @@ func hasUnfinishedMessages(messages []message.Message) bool {
|
||||
return false
|
||||
}
|
||||
for _, msg := range messages {
|
||||
if !msg.Finished {
|
||||
if !msg.IsFinished() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
lastMessage := messages[len(messages)-1]
|
||||
return lastMessage.Role != message.Assistant
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *messagesCmp) renderMessageWithToolCall(content string, tools []message.ToolCall, futureMessages []message.Message) string {
|
||||
@@ -205,7 +208,7 @@ func (m *messagesCmp) renderMessageWithToolCall(content string, tools []message.
|
||||
findToolResult := func(toolCallID string, messages []message.Message) *message.ToolResult {
|
||||
for _, msg := range messages {
|
||||
if msg.Role == message.Tool {
|
||||
for _, result := range msg.ToolResults {
|
||||
for _, result := range msg.ToolResults() {
|
||||
if result.ToolCallID == toolCallID {
|
||||
return &result
|
||||
}
|
||||
@@ -257,7 +260,7 @@ func (m *messagesCmp) renderMessageWithToolCall(content string, tools []message.
|
||||
taskSessionMessages, _ := m.app.Messages.List(toolCall.ID)
|
||||
for _, msg := range taskSessionMessages {
|
||||
if msg.Role == message.Assistant {
|
||||
for _, toolCall := range msg.ToolCalls {
|
||||
for _, toolCall := range msg.ToolCalls() {
|
||||
toolHeader := lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Foreground(styles.Blue).
|
||||
@@ -304,11 +307,11 @@ func (m *messagesCmp) renderMessageWithToolCall(content string, tools []message.
|
||||
}
|
||||
|
||||
for _, msg := range futureMessages {
|
||||
if msg.Content != "" {
|
||||
if msg.Content().String() != "" {
|
||||
break
|
||||
}
|
||||
|
||||
for _, toolCall := range msg.ToolCalls {
|
||||
for _, toolCall := range msg.ToolCalls() {
|
||||
toolOutput := renderTool(toolCall)
|
||||
allParts = append(allParts, " "+strings.ReplaceAll(toolOutput, "\n", "\n "))
|
||||
|
||||
@@ -339,10 +342,10 @@ func (m *messagesCmp) renderView() {
|
||||
|
||||
prevMessageWasUser := false
|
||||
for inx, msg := range m.messages {
|
||||
content := msg.Content
|
||||
content := msg.Content().String()
|
||||
if content != "" || prevMessageWasUser {
|
||||
if msg.Thinking != "" && content == "" {
|
||||
content = msg.Thinking
|
||||
if msg.ReasoningContent().String() != "" && content == "" {
|
||||
content = msg.ReasoningContent().String()
|
||||
} else if content == "" {
|
||||
content = "..."
|
||||
}
|
||||
@@ -367,14 +370,14 @@ func (m *messagesCmp) renderView() {
|
||||
EmbeddedText: borderText(msg.Role, currentMessage),
|
||||
},
|
||||
)
|
||||
if len(msg.ToolCalls) > 0 {
|
||||
content = m.renderMessageWithToolCall(content, msg.ToolCalls, m.messages[inx+1:])
|
||||
if len(msg.ToolCalls()) > 0 {
|
||||
content = m.renderMessageWithToolCall(content, msg.ToolCalls(), m.messages[inx+1:])
|
||||
}
|
||||
stringMessages = append(stringMessages, content)
|
||||
currentMessage++
|
||||
displayedMsgCount++
|
||||
}
|
||||
if msg.Role == message.User && msg.Content != "" {
|
||||
if msg.Role == message.User && msg.Content().String() != "" {
|
||||
prevMessageWasUser = true
|
||||
} else {
|
||||
prevMessageWasUser = false
|
||||
@@ -398,6 +401,57 @@ func (m *messagesCmp) Blur() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *messagesCmp) projectDiagnostics() string {
|
||||
errorDiagnostics := []protocol.Diagnostic{}
|
||||
warnDiagnostics := []protocol.Diagnostic{}
|
||||
hintDiagnostics := []protocol.Diagnostic{}
|
||||
infoDiagnostics := []protocol.Diagnostic{}
|
||||
for _, client := range m.app.LSPClients {
|
||||
for _, d := range client.GetDiagnostics() {
|
||||
for _, diag := range d {
|
||||
switch diag.Severity {
|
||||
case protocol.SeverityError:
|
||||
errorDiagnostics = append(errorDiagnostics, diag)
|
||||
case protocol.SeverityWarning:
|
||||
warnDiagnostics = append(warnDiagnostics, diag)
|
||||
case protocol.SeverityHint:
|
||||
hintDiagnostics = append(hintDiagnostics, diag)
|
||||
case protocol.SeverityInformation:
|
||||
infoDiagnostics = append(infoDiagnostics, diag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errorDiagnostics) == 0 && len(warnDiagnostics) == 0 && len(hintDiagnostics) == 0 && len(infoDiagnostics) == 0 {
|
||||
if time.Since(m.timeLoaded) < time.Second*10 {
|
||||
return "Loading diagnostics..."
|
||||
}
|
||||
return "No diagnostics"
|
||||
}
|
||||
|
||||
diagnostics := []string{}
|
||||
|
||||
if len(errorDiagnostics) > 0 {
|
||||
errStr := lipgloss.NewStyle().Foreground(styles.Error).Render(fmt.Sprintf("%s %d", styles.ErrorIcon, len(errorDiagnostics)))
|
||||
diagnostics = append(diagnostics, errStr)
|
||||
}
|
||||
if len(warnDiagnostics) > 0 {
|
||||
warnStr := lipgloss.NewStyle().Foreground(styles.Warning).Render(fmt.Sprintf("%s %d", styles.WarningIcon, len(warnDiagnostics)))
|
||||
diagnostics = append(diagnostics, warnStr)
|
||||
}
|
||||
if len(hintDiagnostics) > 0 {
|
||||
hintStr := lipgloss.NewStyle().Foreground(styles.Text).Render(fmt.Sprintf("%s %d", styles.HintIcon, len(hintDiagnostics)))
|
||||
diagnostics = append(diagnostics, hintStr)
|
||||
}
|
||||
if len(infoDiagnostics) > 0 {
|
||||
infoStr := lipgloss.NewStyle().Foreground(styles.Peach).Render(fmt.Sprintf("%s %d", styles.InfoIcon, len(infoDiagnostics)))
|
||||
diagnostics = append(diagnostics, infoStr)
|
||||
}
|
||||
|
||||
return strings.Join(diagnostics, " ")
|
||||
}
|
||||
|
||||
func (m *messagesCmp) BorderText() map[layout.BorderPosition]string {
|
||||
title := m.session.Title
|
||||
titleWidth := m.width / 2
|
||||
@@ -409,7 +463,7 @@ func (m *messagesCmp) BorderText() map[layout.BorderPosition]string {
|
||||
}
|
||||
borderTest := map[layout.BorderPosition]string{
|
||||
layout.TopLeftBorder: title,
|
||||
layout.BottomRightBorder: formatTokensAndCost(m.session.CompletionTokens+m.session.PromptTokens, m.session.Cost),
|
||||
layout.BottomRightBorder: m.projectDiagnostics(),
|
||||
}
|
||||
if hasUnfinishedMessages(m.messages) {
|
||||
borderTest[layout.BottomLeftBorder] = lipgloss.NewStyle().Foreground(styles.Peach).Render("Thinking...")
|
||||
@@ -442,6 +496,7 @@ func (m *messagesCmp) SetSize(width int, height int) {
|
||||
}
|
||||
|
||||
func (m *messagesCmp) Init() tea.Cmd {
|
||||
m.timeLoaded = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -245,4 +245,3 @@ func NewSessionsCmp(app *app.App) SessionsCmp {
|
||||
focused: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,11 @@ const (
|
||||
UserIcon string = ""
|
||||
|
||||
CheckIcon string = "✓"
|
||||
ErrorIcon string = "✗"
|
||||
ErrorIcon string = ""
|
||||
WarningIcon string = ""
|
||||
InfoIcon string = ""
|
||||
HintIcon string = ""
|
||||
SpinnerIcon string = "..."
|
||||
|
||||
SleepIcon string = ""
|
||||
BugIcon string = ""
|
||||
SleepIcon string = ""
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user