Skip to content

Commit b9f81a4

Browse files
committed
Fix for issue #440
1 parent 295a309 commit b9f81a4

File tree

3 files changed

+143
-3
lines changed

3 files changed

+143
-3
lines changed

internal/inputhandlers/cleanser.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package inputhandlers
33
import (
44
"strings"
55
"unicode"
6+
"unicode/utf8"
67

78
"github.com/GoMudEngine/GoMud/internal/connections"
89
"github.com/GoMudEngine/GoMud/internal/term"
@@ -28,7 +29,16 @@ func CleanserInputHandler(clientInput *connections.ClientInput, sharedState map[
2829
// send backspace, space, backspace
2930
if len(clientInput.Buffer) > 0 {
3031
connections.SendTo([]byte{term.ASCII_BACKSPACE, term.ASCII_SPACE, term.ASCII_BACKSPACE}, clientInput.ConnectionId)
31-
clientInput.Buffer = clientInput.Buffer[:len(clientInput.Buffer)-1]
32+
33+
// Handle UTF-8 properly by removing the last complete character (rune)
34+
bufferStr := string(clientInput.Buffer)
35+
if len(bufferStr) > 0 {
36+
// Find the start of the last rune
37+
_, size := utf8.DecodeLastRune(clientInput.Buffer)
38+
if size > 0 {
39+
clientInput.Buffer = clientInput.Buffer[:len(clientInput.Buffer)-size]
40+
}
41+
}
3242
}
3343
clientInput.DataIn = clientInput.DataIn[:len(clientInput.DataIn)-1]
3444
return true
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package inputhandlers
2+
3+
import (
4+
"testing"
5+
"unicode/utf8"
6+
7+
"github.com/GoMudEngine/GoMud/internal/connections"
8+
"github.com/GoMudEngine/GoMud/internal/term"
9+
)
10+
11+
func TestCleanserInputHandler_UTF8Backspace(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
initialBuffer string
15+
expectedBuffer string
16+
description string
17+
}{
18+
{
19+
name: "ASCII backspace",
20+
initialBuffer: "hello",
21+
expectedBuffer: "hell",
22+
description: "Should remove single ASCII character",
23+
},
24+
{
25+
name: "UTF8 2-byte character",
26+
initialBuffer: "hellö", // ö is 2 bytes in UTF-8
27+
expectedBuffer: "hell",
28+
description: "Should remove complete 2-byte UTF-8 character",
29+
},
30+
{
31+
name: "UTF8 3-byte character",
32+
initialBuffer: "hello€", // € is 3 bytes in UTF-8
33+
expectedBuffer: "hello",
34+
description: "Should remove complete 3-byte UTF-8 character",
35+
},
36+
{
37+
name: "UTF8 4-byte character",
38+
initialBuffer: "hello🚀", // 🚀 is 4 bytes in UTF-8
39+
expectedBuffer: "hello",
40+
description: "Should remove complete 4-byte UTF-8 character",
41+
},
42+
{
43+
name: "Mixed UTF8 characters",
44+
initialBuffer: "héllö🚀",
45+
expectedBuffer: "héllö",
46+
description: "Should remove last character from mixed UTF-8 string",
47+
},
48+
{
49+
name: "Empty buffer",
50+
initialBuffer: "",
51+
expectedBuffer: "",
52+
description: "Should handle empty buffer gracefully",
53+
},
54+
}
55+
56+
for _, tt := range tests {
57+
t.Run(tt.name, func(t *testing.T) {
58+
// Setup
59+
clientInput := &connections.ClientInput{
60+
ConnectionId: 1,
61+
DataIn: []byte{term.ASCII_BACKSPACE}, // Simulate backspace input
62+
Buffer: []byte(tt.initialBuffer),
63+
EnterPressed: false,
64+
}
65+
sharedState := make(map[string]any)
66+
67+
// Verify initial buffer is valid UTF-8
68+
if !utf8.Valid(clientInput.Buffer) {
69+
t.Fatalf("Initial buffer is not valid UTF-8: %q", tt.initialBuffer)
70+
}
71+
72+
// Call the handler
73+
CleanserInputHandler(clientInput, sharedState)
74+
75+
// Verify result
76+
result := string(clientInput.Buffer)
77+
if result != tt.expectedBuffer {
78+
t.Errorf("Expected buffer %q, got %q", tt.expectedBuffer, result)
79+
}
80+
81+
// Verify the resulting buffer is still valid UTF-8
82+
if !utf8.Valid(clientInput.Buffer) {
83+
t.Errorf("Resulting buffer is not valid UTF-8: %q (bytes: %v)", result, clientInput.Buffer)
84+
}
85+
86+
// Verify BSPressed flag was set
87+
if !clientInput.BSPressed {
88+
t.Error("Expected BSPressed to be true")
89+
}
90+
})
91+
}
92+
}
93+
94+
func TestCleanserInputHandler_NoBackspace(t *testing.T) {
95+
// Test that normal input is handled correctly
96+
clientInput := &connections.ClientInput{
97+
ConnectionId: 1,
98+
DataIn: []byte("hello🚀"), // Multi-byte UTF-8 input
99+
Buffer: []byte("existing"),
100+
EnterPressed: false,
101+
}
102+
sharedState := make(map[string]any)
103+
104+
CleanserInputHandler(clientInput, sharedState)
105+
106+
// Should append the new input to existing buffer
107+
expected := "existinghello🚀"
108+
result := string(clientInput.Buffer)
109+
if result != expected {
110+
t.Errorf("Expected buffer %q, got %q", expected, result)
111+
}
112+
113+
// Verify the resulting buffer is valid UTF-8
114+
if !utf8.Valid(clientInput.Buffer) {
115+
t.Errorf("Resulting buffer is not valid UTF-8: %q", result)
116+
}
117+
118+
// Verify BSPressed flag was not set
119+
if clientInput.BSPressed {
120+
t.Error("Expected BSPressed to be false")
121+
}
122+
}

internal/inputhandlers/login_prompt_handler.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"net/mail"
77
"strings"
8+
"unicode/utf8"
89

910
"github.com/GoMudEngine/GoMud/internal/configs"
1011
"github.com/GoMudEngine/GoMud/internal/connections"
@@ -201,8 +202,15 @@ func CreatePromptHandler(steps []*PromptStep, onComplete CompletionFunc) connect
201202

202203
if clientInput.BSPressed && len(clientInput.Buffer) > 0 {
203204

204-
// Handle Backspace
205-
clientInput.Buffer = clientInput.Buffer[:len(clientInput.Buffer)-1]
205+
// Handle Backspace - properly handle UTF-8 multi-byte characters
206+
bufferStr := string(clientInput.Buffer)
207+
if len(bufferStr) > 0 {
208+
// Find the start of the last rune
209+
_, size := utf8.DecodeLastRune(clientInput.Buffer)
210+
if size > 0 {
211+
clientInput.Buffer = clientInput.Buffer[:len(clientInput.Buffer)-size]
212+
}
213+
}
206214
//connections.SendTo([]byte{term.ASCII_BACKSPACE, term.ASCII_SPACE, term.ASCII_BACKSPACE}, clientInput.ConnectionId)
207215

208216
} else if !clientInput.BSPressed && len(clientInput.DataIn) > 0 && clientInput.DataIn[0] >= 32 {

0 commit comments

Comments
 (0)