Skip to content

Commit f62da6e

Browse files
committed
Fixed #15: relax character index on last line
1 parent 6bd1522 commit f62da6e

File tree

4 files changed

+74
-64
lines changed

4 files changed

+74
-64
lines changed

Diff for: handler/handler.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ import (
2020

2121
var globalCliPath string
2222
var enableLogging bool
23+
var asyncProcessing bool
2324

2425
// Setup initializes global variables.
25-
func Setup(cliPath string, _enableLogging bool) {
26+
func Setup(cliPath string, _enableLogging bool, _asyncProcessing bool) {
2627
globalCliPath = cliPath
2728
enableLogging = _enableLogging
29+
asyncProcessing = _asyncProcessing
2830
}
2931

3032
// CLangdStarter starts clangd and returns its stdin/out/err
@@ -44,7 +46,10 @@ func NewInoHandler(stdin io.ReadCloser, stdout io.WriteCloser, logStreams *Strea
4446
}
4547
handler.startClangd()
4648
stdStream := jsonrpc2.NewBufferedStream(logStreams.AttachStdInOut(stdin, stdout), jsonrpc2.VSCodeObjectCodec{})
47-
stdHandler := jsonrpc2.AsyncHandler(jsonrpc2.HandlerWithError(handler.FromStdio))
49+
var stdHandler jsonrpc2.Handler = jsonrpc2.HandlerWithError(handler.FromStdio)
50+
if asyncProcessing {
51+
stdHandler = jsonrpc2.AsyncHandler(stdHandler)
52+
}
4853
handler.StdioConn = jsonrpc2.NewConn(context.Background(), stdStream, stdHandler)
4954
if enableLogging {
5055
log.Println("Initial board configuration:", board)
@@ -633,7 +638,7 @@ func (handler *InoHandler) cpp2inoCompletionList(list *lsp.CompletionList, uri l
633638
if data, ok := handler.data[uri]; ok {
634639
newItems := make([]lsp.CompletionItem, 0, len(list.Items))
635640
for _, item := range list.Items {
636-
if (!strings.HasPrefix(item.InsertText, "_")) {
641+
if !strings.HasPrefix(item.InsertText, "_") {
637642
if item.TextEdit != nil {
638643
r := &item.TextEdit.Range
639644
r.Start.Line = data.sourceLineMap[r.Start.Line]
@@ -642,7 +647,7 @@ func (handler *InoHandler) cpp2inoCompletionList(list *lsp.CompletionList, uri l
642647
newItems = append(newItems, item)
643648
}
644649
}
645-
list.Items = newItems;
650+
list.Items = newItems
646651
}
647652
}
648653

Diff for: handler/sourcemap.go

+39-39
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,13 @@ func copyMappings(sourceLineMap, targetLineMap, newMappings map[int]int) {
116116

117117
// OutOfRangeError returned if one attempts to access text out of its range
118118
type OutOfRangeError struct {
119-
Max int
120-
Req lsp.Position
119+
Type string
120+
Max int
121+
Req int
121122
}
122123

123124
func (oor OutOfRangeError) Error() string {
124-
return fmt.Sprintf("text access out of range: max=%d requested=%d", oor.Max, oor.Req)
125+
return fmt.Sprintf("%s access out of range: max=%d requested=%d", oor.Type, oor.Max, oor.Req)
125126
}
126127

127128
func applyTextChange(text string, rang lsp.Range, insertText string) (res string, err error) {
@@ -139,37 +140,41 @@ func applyTextChange(text string, rang lsp.Range, insertText string) (res string
139140

140141
// getOffset computes the offset in the text expressed by the lsp.Position.
141142
// Returns OutOfRangeError if the position is out of range.
142-
func getOffset(text string, pos lsp.Position) (off int, err error) {
143-
// find line
144-
lineOffset := getLineOffset(text, pos.Line)
145-
if lineOffset < 0 {
146-
return -1, OutOfRangeError{len(text), pos}
143+
func getOffset(text string, pos lsp.Position) (int, error) {
144+
// Find line
145+
lineOffset, err := getLineOffset(text, pos.Line)
146+
if err != nil {
147+
return -1, err
148+
}
149+
character := pos.Character
150+
if character == 0 {
151+
return lineOffset, nil
147152
}
148-
off = lineOffset
149153

150-
// walk towards the character
151-
var charFound bool
152-
for offset, c := range text[off:] {
154+
// Find the character and return its offset within the text
155+
var count = len(text[lineOffset:])
156+
for offset, c := range text[lineOffset:] {
157+
if character == offset {
158+
// We've found the character
159+
return lineOffset + offset, nil
160+
}
153161
if c == '\n' {
154162
// We've reached the end of line. LSP spec says we should default back to the line length.
155163
// See https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#position
156-
off += offset
157-
charFound = true
158-
break
159-
}
160-
161-
// we've fond the character
162-
if offset == pos.Character {
163-
off += offset
164-
charFound = true
164+
if character > offset {
165+
return lineOffset + offset, nil
166+
}
167+
count = offset
165168
break
166169
}
167170
}
168-
if !charFound {
169-
return -1, OutOfRangeError{Max: len(text), Req: pos}
171+
if character > 0 {
172+
// We've reached the end of the last line. Default to the text length (see above).
173+
return len(text), nil
170174
}
171175

172-
return off, nil
176+
// We haven't found the character in the text (character index was negative)
177+
return -1, OutOfRangeError{"Character", count, character}
173178
}
174179

175180
// getLineOffset finds the offset/position of the beginning of a line within the text.
@@ -178,27 +183,22 @@ func getOffset(text string, pos lsp.Position) (off int, err error) {
178183
// getLineOffset(text, 0) == 0
179184
// getLineOffset(text, 1) == 4
180185
// getLineOffset(text, 2) == 11
181-
func getLineOffset(text string, line int) int {
182-
if line < 0 {
183-
return -1
184-
}
186+
func getLineOffset(text string, line int) (int, error) {
185187
if line == 0 {
186-
return 0
188+
return 0, nil
187189
}
188190

189-
// find the line and return its offset within the text
191+
// Find the line and return its offset within the text
190192
var count int
191193
for offset, c := range text {
192-
if c != '\n' {
193-
continue
194-
}
195-
196-
count++
197-
if count == line {
198-
return offset + 1
194+
if c == '\n' {
195+
count++
196+
if count == line {
197+
return offset + 1, nil
198+
}
199199
}
200200
}
201201

202-
// we didn't find the line in the text
203-
return -1
202+
// We haven't found the line in the text
203+
return -1, OutOfRangeError{"Line", count, line}
204204
}

Diff for: handler/sourcemap_test.go

+25-20
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ func TestApplyTextChange(t *testing.T) {
122122
{
123123
"foo\nbar\nbaz\n!",
124124
lsp.Range{
125-
// out of range start offset
126125
Start: lsp.Position{Line: 0, Character: 100},
127126
End: lsp.Position{Line: 2, Character: 0},
128127
},
@@ -139,18 +138,18 @@ func TestApplyTextChange(t *testing.T) {
139138
},
140139
"i",
141140
"",
142-
OutOfRangeError{13, lsp.Position{Line: 20, Character: 0}},
141+
OutOfRangeError{"Line", 3, 20},
143142
},
144143
{
145144
"foo\nbar\nbaz\n!",
146145
lsp.Range{
147-
// out of range start offset
146+
// out of range end offset
148147
Start: lsp.Position{Line: 0, Character: 0},
149148
End: lsp.Position{Line: 20, Character: 0},
150149
},
151150
"i",
152151
"",
153-
OutOfRangeError{13, lsp.Position{Line: 20, Character: 0}},
152+
OutOfRangeError{"Line", 3, 20},
154153
},
155154
}
156155

@@ -183,8 +182,10 @@ func TestGetOffset(t *testing.T) {
183182
{"foo\nfoobar\nbaz", 1, 3, 7, nil},
184183
{"foo\nba\nr\nbaz\n!", 3, 0, 9, nil},
185184
{"foo\nba\nr\nbaz\n!", 1, 10, 6, nil},
186-
{"foo\nba\nr\nbaz\n!", -1, 0, -1, OutOfRangeError{14, lsp.Position{Line: -1, Character: 0}}},
187-
{"foo\nba\nr\nbaz\n!", 4, 20, -1, OutOfRangeError{14, lsp.Position{Line: 4, Character: 20}}},
185+
{"foo\nba\nr\nbaz\n!", -1, 0, -1, OutOfRangeError{"Line", 4, -1}},
186+
{"foo\nba\nr\nbaz\n!", 1, -1, -1, OutOfRangeError{"Character", 2, -1}},
187+
{"foo\nba\nr\nbaz\n!", 4, 20, 14, nil},
188+
{"foo\nba\nr\nbaz!\n", 4, 0, 14, nil},
188189
}
189190

190191
for _, test := range tests {
@@ -203,26 +204,30 @@ func TestGetOffset(t *testing.T) {
203204

204205
func TestGetLineOffset(t *testing.T) {
205206
tests := []struct {
206-
Text string
207-
Line int
208-
Offset int
207+
Text string
208+
Line int
209+
Exp int
210+
Err error
209211
}{
210-
{"foo\nfoobar\nbaz", 0, 0},
211-
{"foo\nfoobar\nbaz", 1, 4},
212-
{"foo\nfoobar\nbaz", 2, 11},
213-
{"foo\nfoobar\nbaz", 3, -1},
214-
{"foo\nba\nr\nbaz\n!", 3, 9},
215-
{"foo\nba\nr\nbaz\n!", -1, -1},
216-
{"foo\nba\nr\nbaz\n!", 20, -1},
212+
{"foo\nfoobar\nbaz", 0, 0, nil},
213+
{"foo\nfoobar\nbaz", 1, 4, nil},
214+
{"foo\nfoobar\nbaz", 2, 11, nil},
215+
{"foo\nfoobar\nbaz", 3, -1, OutOfRangeError{"Line", 2, 3}},
216+
{"foo\nba\nr\nbaz\n!", 3, 9, nil},
217+
{"foo\nba\nr\nbaz\n!", -1, -1, OutOfRangeError{"Line", 4, -1}},
218+
{"foo\nba\nr\nbaz\n!", 20, -1, OutOfRangeError{"Line", 4, 20}},
217219
}
218220

219221
for _, test := range tests {
220222
st := strings.Replace(test.Text, "\n", "\\n", -1)
221223

222-
t.Logf("getLineOffset(\"%s\", %d) == %d", st, test.Line, test.Offset)
223-
act := getLineOffset(test.Text, test.Line)
224-
if act != test.Offset {
225-
t.Errorf("getLineOffset(\"%s\", %d) != %d, got %d instead", st, test.Line, test.Offset, act)
224+
t.Logf("getLineOffset(\"%s\", %d) == %d", st, test.Line, test.Exp)
225+
act, err := getLineOffset(test.Text, test.Line)
226+
if act != test.Exp {
227+
t.Errorf("getLineOffset(\"%s\", %d) != %d, got %d instead", st, test.Line, test.Exp, act)
228+
}
229+
if err != test.Err {
230+
t.Errorf("getLineOffset(\"%s\", %d) error != %v, got %v instead", st, test.Line, test.Err, err)
226231
}
227232
}
228233
}

Diff for: main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func main() {
4242
log.SetOutput(os.Stderr)
4343
}
4444

45-
handler.Setup(cliPath, enableLogging)
45+
handler.Setup(cliPath, enableLogging, true)
4646
initialBoard := handler.Board{Fqbn: initialFqbn, Name: initialBoardName}
4747
inoHandler := handler.NewInoHandler(os.Stdin, os.Stdout, logStreams, startClangd, initialBoard)
4848
defer inoHandler.StopClangd()

0 commit comments

Comments
 (0)