File: source.go

package info (click to toggle)
golang-github-antonmedv-expr 1.8.9-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 4,524 kB
  • sloc: makefile: 6
file content (95 lines) | stat: -rwxr-xr-x 2,165 bytes parent folder | download
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
package file

import (
	"encoding/json"
	"strings"
	"unicode/utf8"
)

type Source struct {
	contents    []rune
	lineOffsets []int32
}

func NewSource(contents string) *Source {
	s := &Source{
		contents: []rune(contents),
	}
	s.updateOffsets()
	return s
}

func (s *Source) MarshalJSON() ([]byte, error) {
	return json.Marshal(s.contents)
}

func (s *Source) UnmarshalJSON(b []byte) error {
	contents := make([]rune, 0)
	err := json.Unmarshal(b, &contents)
	if err != nil {
		return err
	}

	s.contents = contents
	s.updateOffsets()
	return nil
}

func (s *Source) Content() string {
	return string(s.contents)
}

func (s *Source) Snippet(line int) (string, bool) {
	charStart, found := s.findLineOffset(line)
	if !found || len(s.contents) == 0 {
		return "", false
	}
	charEnd, found := s.findLineOffset(line + 1)
	if found {
		return string(s.contents[charStart : charEnd-1]), true
	}
	return string(s.contents[charStart:]), true
}

// updateOffsets compute line offsets up front as they are referred to frequently.
func (s *Source) updateOffsets() {
	lines := strings.Split(string(s.contents), "\n")
	offsets := make([]int32, len(lines))
	var offset int32
	for i, line := range lines {
		offset = offset + int32(utf8.RuneCountInString(line)) + 1
		offsets[int32(i)] = offset
	}
	s.lineOffsets = offsets
}

// findLineOffset returns the offset where the (1-indexed) line begins,
// or false if line doesn't exist.
func (s *Source) findLineOffset(line int) (int32, bool) {
	if line == 1 {
		return 0, true
	} else if line > 1 && line <= len(s.lineOffsets) {
		offset := s.lineOffsets[line-2]
		return offset, true
	}
	return -1, false
}

// findLine finds the line that contains the given character offset and
// returns the line number and offset of the beginning of that line.
// Note that the last line is treated as if it contains all offsets
// beyond the end of the actual source.
func (s *Source) findLine(characterOffset int32) (int32, int32) {
	var line int32 = 1
	for _, lineOffset := range s.lineOffsets {
		if lineOffset > characterOffset {
			break
		} else {
			line++
		}
	}
	if line == 1 {
		return line, 0
	}
	return line, s.lineOffsets[line-2]
}