1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
package lsp
import (
"bytes"
"strings"
"testing"
"time"
)
// Ensure in-editor chat respects general.context_mode by adding window/full-file context.
func TestChat_RespectsContextModeWindow(t *testing.T) {
s := newTestServer()
// Configure window mode with small window
s.contextMode = "window"
s.windowLines = 2
s.maxContextTokens = 2000
cap := &captureLLM{}
s.llmClient = cap
var out bytes.Buffer
s.out = &out
uri := "file:///ctx.go"
// Build a small file where the last line triggers chat
src := "package main\nline2 context\nwhat?>\n"
s.setDocument(uri, src)
s.detectAndHandleChat(uri)
// Wait briefly for async goroutine to call Chat
for i := 0; i < 40 && len(cap.msgs) == 0; i++ {
time.Sleep(10 * time.Millisecond)
}
if len(cap.msgs) == 0 {
t.Fatalf("expected Chat to be called")
}
// Expect first system, then an extra context user message, then history ending with prompt
if cap.msgs[0].Role != "system" {
t.Fatalf("first message should be system, got %q", cap.msgs[0].Role)
}
if len(cap.msgs) < 3 {
t.Fatalf("expected at least 3 messages (system, extra, user prompt), got %d", len(cap.msgs))
}
extra := cap.msgs[1]
if extra.Role != "user" || !strings.HasPrefix(extra.Content, "Additional context:\n") {
t.Fatalf("second message should be user extra context, got role=%q content=%q", extra.Role, extra.Content)
}
if !strings.Contains(extra.Content, "line2 context") {
t.Fatalf("extra context should include window text; got %q", extra.Content)
}
last := cap.msgs[len(cap.msgs)-1]
if last.Role != "user" || last.Content != "what?" {
t.Fatalf("last message should be current prompt user, got %+v", last)
}
}
func TestChat_ContextModeMinimal_NoExtra(t *testing.T) {
s := newTestServer()
s.contextMode = "minimal"
s.maxContextTokens = 2000
cap := &captureLLM{}
s.llmClient = cap
var out bytes.Buffer
s.out = &out
uri := "file:///ctx2.go"
s.setDocument(uri, "package main\nhelp?>\n")
s.detectAndHandleChat(uri)
for i := 0; i < 40 && len(cap.msgs) == 0; i++ {
time.Sleep(10 * time.Millisecond)
}
if len(cap.msgs) != 2 {
t.Fatalf("expected exactly 2 messages (system + user prompt), got %d", len(cap.msgs))
}
if cap.msgs[0].Role != "system" || cap.msgs[1].Role != "user" || cap.msgs[1].Content != "help?" {
t.Fatalf("unexpected messages: %+v", cap.msgs)
}
}
func TestChat_ContextModeAlwaysFull_AddsExtra(t *testing.T) {
s := newTestServer()
s.contextMode = "always-full"
s.maxContextTokens = 2000
cap := &captureLLM{}
s.llmClient = cap
var out bytes.Buffer
s.out = &out
uri := "file:///ctx3.go"
s.setDocument(uri, "package main\nline2\nhelp?>\n")
s.detectAndHandleChat(uri)
for i := 0; i < 40 && len(cap.msgs) == 0; i++ {
time.Sleep(10 * time.Millisecond)
}
if len(cap.msgs) < 3 {
t.Fatalf("expected >=3 messages (system, extra, user prompt), got %d", len(cap.msgs))
}
if cap.msgs[1].Role != "user" || !strings.HasPrefix(cap.msgs[1].Content, "Additional context:\n") {
t.Fatalf("second message should be user extra context, got role=%q content=%q", cap.msgs[1].Role, cap.msgs[1].Content)
}
if !strings.Contains(cap.msgs[1].Content, "package main") {
t.Fatalf("extra context should include full file, got %q", cap.msgs[1].Content)
}
if last := cap.msgs[len(cap.msgs)-1]; last.Role != "user" || last.Content != "help?" {
t.Fatalf("last message should be the current prompt, got %+v", last)
}
}
func TestChat_ContextModeFileOnNewFunc_NoExtraWithoutSignature(t *testing.T) {
s := newTestServer()
s.contextMode = "file-on-new-func"
s.maxContextTokens = 2000
cap := &captureLLM{}
s.llmClient = cap
var out bytes.Buffer
s.out = &out
uri := "file:///ctx4.go"
s.setDocument(uri, "package main\nhelp?>\n")
s.detectAndHandleChat(uri)
for i := 0; i < 40 && len(cap.msgs) == 0; i++ {
time.Sleep(10 * time.Millisecond)
}
if len(cap.msgs) != 2 {
t.Fatalf("expected exactly 2 messages (system + user prompt), got %d", len(cap.msgs))
}
}
func TestChat_ContextModeFileOnNewFunc_WithSignature_AddsExtra(t *testing.T) {
s := newTestServer()
s.contextMode = "file-on-new-func"
s.maxContextTokens = 2000
cap := &captureLLM{}
s.llmClient = cap
var out bytes.Buffer
s.out = &out
uri := "file:///ctx5.go"
// Signature without '{' yet; chat prompt appears before the body, so newFunc=true
src := "package main\n\nfunc add(x int) int\nhelp?>\n"
s.setDocument(uri, src)
s.detectAndHandleChat(uri)
for i := 0; i < 40 && len(cap.msgs) == 0; i++ {
time.Sleep(10 * time.Millisecond)
}
if len(cap.msgs) < 3 {
t.Fatalf("expected >=3 messages (system, extra, user prompt), got %d", len(cap.msgs))
}
if cap.msgs[1].Role != "user" || !strings.HasPrefix(cap.msgs[1].Content, "Additional context:\n") {
t.Fatalf("second message should be user extra context, got role=%q content=%q", cap.msgs[1].Role, cap.msgs[1].Content)
}
if !strings.Contains(cap.msgs[1].Content, "func add(x int) int") {
t.Fatalf("extra context should include full file or signature, got %q", cap.msgs[1].Content)
}
}
|