all repos

clerk @ 5c5108ba9a1f581c4b02f1983b72a26af7a06730

missing tooling for ledger/hledger

clerk/internal/lsp/lsputil/utf16.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
lsp: semantic highlights, 2 months ago
1
package lsputil
2
3
import "unicode/utf8"
4
5
// Utf16Col returns the UTF-16 code unit column (0-based) for a byte offset
6
// within the given line content (without newline).
7
func Utf16Col(line string, byteOffset int) int {
8
	if byteOffset <= 0 {
9
		return 0
10
	}
11
	if byteOffset > len(line) {
12
		byteOffset = len(line)
13
	}
14
	col := 0
15
	for i := 0; i < byteOffset; {
16
		r, size := utf8.DecodeRuneInString(line[i:])
17
		if r == utf8.RuneError && size <= 1 {
18
			break
19
		}
20
		col += utf16Len(r)
21
		i += size
22
	}
23
	return col
24
}
25
26
// Utf16Len returns the UTF-16 code unit length of content[offset:end].
27
func Utf16Len(content string, offset, end int) int {
28
	if offset < 0 {
29
		offset = 0
30
	}
31
	if end > len(content) {
32
		end = len(content)
33
	}
34
	if offset >= end {
35
		return 0
36
	}
37
	n := 0
38
	for i := offset; i < end; {
39
		r, size := utf8.DecodeRuneInString(content[i:])
40
		if r == utf8.RuneError && size <= 1 {
41
			break
42
		}
43
		n += utf16Len(r)
44
		i += size
45
	}
46
	return n
47
}
48
49
// LineCol converts a byte offset in content to 0-based line number and
50
// UTF-16 code unit column.
51
func LineCol(content string, offset int) (line int, col int) {
52
	if offset <= 0 {
53
		return 0, 0
54
	}
55
	if offset > len(content) {
56
		offset = len(content)
57
	}
58
	lineStart := 0
59
	lineNum := 0
60
	for i := 0; i < len(content); {
61
		r, size := utf8.DecodeRuneInString(content[i:])
62
		if r == utf8.RuneError && size <= 1 {
63
			break
64
		}
65
		if r == '\n' {
66
			if offset < i+size {
67
				col = Utf16Col(content[lineStart:offset], offset-lineStart)
68
				return lineNum, col
69
			}
70
			lineStart = i + size
71
			lineNum++
72
			i += size
73
			continue
74
		}
75
		if r == '\r' {
76
			// skip \r\n
77
			if i+1 < len(content) && content[i+1] == '\n' {
78
				size++
79
			}
80
			if offset < i+size {
81
				col = Utf16Col(content[lineStart:offset], offset-lineStart)
82
				return lineNum, col
83
			}
84
			lineStart = i + size
85
			lineNum++
86
			i += size
87
			continue
88
		}
89
		if i+size > offset {
90
			col = Utf16Col(content[lineStart:offset], offset-lineStart)
91
			return lineNum, col
92
		}
93
		i += size
94
	}
95
	col = Utf16Col(content[lineStart:], len(content)-lineStart)
96
	return lineNum, col
97
}
98
99
// utf16Len returns the number of UTF-16 code units for a rune.
100
func utf16Len(r rune) int {
101
	if r >= 0x10000 && r <= 0x10FFFF {
102
		return 2
103
	}
104
	return 1
105
}