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
158
159
160
161
162
163
164
165
166
167
168
169
170
// Position tracking for editor intelligence integration (line/column mapping)
use ;
/// Position in a source document using multiple coordinate systems.
///
/// This struct tracks a position using three different representations:
/// - **Line/Column**: CommonMark-style 1-based coordinates
/// - **Absolute Offset**: Byte offset from document start
///
/// # Coordinate Systems
///
/// ## Line/Column (Primary for GTK Integration)
/// - `line`: 1-based line number (CommonMark convention)
/// - `column`: 1-based byte offset from the start of the line
///
/// **Important**: `column` is a BYTE offset, not a character offset!
/// - For ASCII: byte offset == character offset
/// - For UTF-8: Multi-byte characters cause divergence
/// - Example: "Tëst" has 'ë' at byte columns 3-4, but char column 2
/// - Example: "🎨" (emoji) occupies 4 bytes but is 1 character
///
/// ## Absolute Offset (For Debugging Only)
/// - `offset`: Absolute byte offset from document start
/// - **Do NOT use** for GTK TextIter positioning!
/// - Use `line` and `column` instead for robust conversion
///
/// # Usage with GTK
///
/// When converting to GTK TextIter:
/// 1. Convert line: `parser_line (1-based)` → `gtk_line (0-based)`
/// 2. Get line text from GTK buffer
/// 3. Convert column: `byte_offset → char_offset` using `char_indices()`
/// 4. Set position: `iter_at_line(gtk_line).set_line_offset(char_offset)`
///
/// See the host editor's cursor-conversion bridge for a reference implementation
/// of byte-to-character offset mapping.
/// A span representing a range in the source document.
///
/// Spans are inclusive of the start position and exclusive of the end position.
/// This matches CommonMark and most parser conventions.
///
/// # Example
///
/// For the text "**bold**":
/// - `start`: Position at the first '*'
/// - `end`: Position after the last '*' (one past the last character)
///
/// # Multi-line Spans
///
/// For multi-line content like code blocks, for example a fenced Rust code block,
/// the inner code might look like:
///
/// ```text
/// fn main() {
/// }
/// ```
///
/// - `start.line`: Line of opening backticks
/// - `end.line`: Line after closing backticks
/// - Columns are byte offsets within their respective lines
/// Convenience helper: compute the absolute byte offset of the start of the
/// given span's starting line.
///
/// This is exposed as a free function to simplify callers that don't have a
/// `Span` method in scope or prefer a function name matching the refactor plan.