1use crate::utils::calculate_indentation_width_default;
4use std::collections::HashSet;
5use std::ops::Range;
6
7fn find_char_boundary(s: &str, byte_idx: usize) -> usize {
14 if byte_idx >= s.len() {
15 return s.len();
16 }
17
18 if s.is_char_boundary(byte_idx) {
20 return byte_idx;
21 }
22
23 let mut pos = byte_idx;
26 while pos > 0 && !s.is_char_boundary(pos) {
27 pos -= 1;
28 }
29 pos
30}
31
32fn byte_to_char_count(s: &str, byte_idx: usize) -> usize {
35 let safe_byte_idx = find_char_boundary(s, byte_idx);
36 s[..safe_byte_idx].chars().count() + 1 }
38
39#[derive(Debug)]
40pub struct LineIndex<'a> {
41 line_starts: Vec<usize>,
42 content: &'a str,
43 code_block_lines: Option<HashSet<usize>>,
44}
45
46impl<'a> LineIndex<'a> {
47 pub fn new(content: &'a str) -> Self {
48 let mut line_starts = vec![0];
49 let mut pos = 0;
50
51 for c in content.chars() {
52 pos += c.len_utf8();
53 if c == '\n' {
54 line_starts.push(pos);
55 }
56 }
57
58 let mut index = Self {
59 line_starts,
60 content,
61 code_block_lines: None,
62 };
63
64 index.compute_code_block_lines();
66
67 index
68 }
69
70 pub fn with_line_starts(content: &'a str, line_starts: Vec<usize>) -> Self {
74 let mut index = Self {
75 line_starts,
76 content,
77 code_block_lines: None,
78 };
79
80 index.compute_code_block_lines();
82
83 index
84 }
85
86 pub fn with_line_starts_and_code_blocks(
91 content: &'a str,
92 line_starts: Vec<usize>,
93 code_block_byte_ranges: &[(usize, usize)],
94 ) -> Self {
95 let mut code_block_lines = HashSet::new();
96
97 for &(block_start, block_end) in code_block_byte_ranges {
98 let start_line = match line_starts.binary_search(&block_start) {
99 Ok(idx) => idx,
100 Err(idx) => idx.saturating_sub(1),
101 };
102 let end_line = if block_end == 0 {
103 0
104 } else {
105 match line_starts.binary_search(&block_end) {
106 Ok(idx) => idx.saturating_sub(1),
108 Err(idx) => idx.saturating_sub(1),
109 }
110 };
111 for line_idx in start_line..=end_line {
112 code_block_lines.insert(line_idx);
113 }
114 }
115
116 Self {
117 line_starts,
118 content,
119 code_block_lines: Some(code_block_lines),
120 }
121 }
122
123 fn get_line(&self, line_idx: usize) -> Option<&'a str> {
126 let start = *self.line_starts.get(line_idx)?;
127 let end = self
128 .line_starts
129 .get(line_idx + 1)
130 .copied()
131 .unwrap_or(self.content.len());
132 let line = &self.content[start..end];
133 let line = line.strip_suffix('\n').unwrap_or(line);
135 let line = line.strip_suffix('\r').unwrap_or(line);
136 Some(line)
137 }
138
139 pub fn line_col_to_byte_range(&self, line: usize, column: usize) -> Range<usize> {
140 let line = line.saturating_sub(1);
141 let line_start = *self.line_starts.get(line).unwrap_or(&self.content.len());
142
143 let current_line = self.get_line(line).unwrap_or("");
144 let char_col = column.saturating_sub(1);
146 let char_count = current_line.chars().count();
147 let safe_char_col = char_col.min(char_count);
148
149 let byte_offset = current_line
151 .char_indices()
152 .nth(safe_char_col)
153 .map_or(current_line.len(), |(idx, _)| idx);
154
155 let start = line_start + byte_offset;
156 start..start
157 }
158
159 pub fn line_col_to_byte_range_with_length(&self, line: usize, column: usize, length: usize) -> Range<usize> {
166 let line = line.saturating_sub(1);
167 let line_start = *self.line_starts.get(line).unwrap_or(&self.content.len());
168 let line_end = self.line_starts.get(line + 1).copied().unwrap_or(self.content.len());
169 let mut current_line = &self.content[line_start..line_end];
170 if let Some(stripped) = current_line.strip_suffix('\n') {
171 current_line = stripped.strip_suffix('\r').unwrap_or(stripped);
172 }
173 if current_line.is_ascii() {
174 let line_len = current_line.len();
175 let start_byte = column.saturating_sub(1).min(line_len);
176 let end_byte = start_byte.saturating_add(length).min(line_len);
177 let start = line_start + start_byte;
178 let end = line_start + end_byte;
179 return start..end;
180 }
181 let char_col = column.saturating_sub(1);
183 let char_count = current_line.chars().count();
184 let safe_char_col = char_col.min(char_count);
185
186 let mut char_indices = current_line.char_indices();
188 let start_byte = char_indices
189 .nth(safe_char_col)
190 .map_or(current_line.len(), |(idx, _)| idx);
191
192 let end_char_col = (safe_char_col + length).min(char_count);
194 let end_byte = current_line
195 .char_indices()
196 .nth(end_char_col)
197 .map_or(current_line.len(), |(idx, _)| idx);
198
199 let start = line_start + start_byte;
200 let end = line_start + end_byte;
201 start..end
202 }
203
204 pub fn whole_line_range(&self, line: usize) -> Range<usize> {
207 let line_idx = line.saturating_sub(1);
208 let start = *self.line_starts.get(line_idx).unwrap_or(&self.content.len());
209 let end = self
210 .line_starts
211 .get(line_idx + 1)
212 .copied()
213 .unwrap_or(self.content.len());
214 start..end
215 }
216
217 pub fn multi_line_range(&self, start_line: usize, end_line: usize) -> Range<usize> {
220 let start_idx = start_line.saturating_sub(1);
221 let end_idx = end_line.saturating_sub(1);
222
223 let start = *self.line_starts.get(start_idx).unwrap_or(&self.content.len());
224 let end = self.line_starts.get(end_idx + 1).copied().unwrap_or(self.content.len());
225 start..end
226 }
227
228 pub fn line_text_range(&self, line: usize, start_col: usize, end_col: usize) -> Range<usize> {
235 let line_idx = line.saturating_sub(1);
236 let line_start = *self.line_starts.get(line_idx).unwrap_or(&self.content.len());
237
238 let current_line = self.get_line(line_idx).unwrap_or("");
240 let char_count = current_line.chars().count();
241
242 let start_char_col = start_col.saturating_sub(1).min(char_count);
244 let end_char_col = end_col.saturating_sub(1).min(char_count);
245
246 let mut char_indices = current_line.char_indices();
247 let start_byte = char_indices
248 .nth(start_char_col)
249 .map_or(current_line.len(), |(idx, _)| idx);
250
251 let end_byte = current_line
252 .char_indices()
253 .nth(end_char_col)
254 .map_or(current_line.len(), |(idx, _)| idx);
255
256 let start = line_start + start_byte;
257 let end = line_start + end_byte.max(start_byte);
258 start..end
259 }
260
261 pub fn line_content_range(&self, line: usize) -> Range<usize> {
264 let line_idx = line.saturating_sub(1);
265 let line_start = *self.line_starts.get(line_idx).unwrap_or(&self.content.len());
266
267 let current_line = self.get_line(line_idx).unwrap_or("");
268 let line_end = line_start + current_line.len();
269 line_start..line_end
270 }
271
272 pub fn get_line_start_byte(&self, line_num: usize) -> Option<usize> {
274 if line_num == 0 {
275 return None; }
277 self.line_starts.get(line_num - 1).copied()
279 }
280
281 pub fn is_code_block(&self, line: usize) -> bool {
283 if let Some(ref code_block_lines) = self.code_block_lines {
284 code_block_lines.contains(&line)
285 } else {
286 self.is_code_fence(line)
288 }
289 }
290
291 pub fn is_code_fence(&self, line: usize) -> bool {
293 self.get_line(line).is_some_and(|l| {
294 let trimmed = l.trim();
295 trimmed.starts_with("```") || trimmed.starts_with("~~~")
296 })
297 }
298
299 pub fn is_tilde_code_block(&self, line: usize) -> bool {
301 self.get_line(line).is_some_and(|l| l.trim().starts_with("~~~"))
302 }
303
304 pub fn get_content(&self) -> &str {
306 self.content
307 }
308
309 fn compute_code_block_lines(&mut self) {
311 let mut code_block_lines = HashSet::new();
312 let lines: Vec<&str> = self.content.lines().collect();
313
314 let mut in_block = false;
316 let mut active_fence_type = ' '; let mut block_indent = 0;
318 let mut block_fence_length = 0;
319 let mut in_markdown_block = false;
320 let mut nested_fence_start = None;
321 let mut nested_fence_end = None;
322
323 for (i, line) in lines.iter().enumerate() {
325 let trimmed = line.trim();
326 let indent = line.len() - trimmed.len();
327
328 if calculate_indentation_width_default(line) >= 4 {
330 code_block_lines.insert(i);
331 continue; }
333
334 if !in_block {
336 if trimmed.starts_with("```") || trimmed.starts_with("~~~") {
338 let char_type = if trimmed.starts_with("```") { '`' } else { '~' };
339 let count = trimmed.chars().take_while(|&c| c == char_type).count();
340 let info_string = if trimmed.len() > count {
341 trimmed[count..].trim()
342 } else {
343 ""
344 };
345
346 in_block = true;
348 active_fence_type = char_type;
349 block_indent = indent;
350 block_fence_length = count;
351 in_markdown_block = info_string == "markdown";
352 nested_fence_start = None;
353 nested_fence_end = None;
354
355 code_block_lines.insert(i);
356 }
357 } else {
358 code_block_lines.insert(i);
360
361 if in_markdown_block && nested_fence_start.is_none() && trimmed.starts_with("```") {
363 let count = trimmed.chars().take_while(|&c| c == '`').count();
365 let remaining = if trimmed.len() > count {
366 trimmed[count..].trim()
367 } else {
368 ""
369 };
370
371 if !remaining.is_empty() {
372 nested_fence_start = Some(i);
373 }
374 }
375
376 if in_markdown_block
378 && nested_fence_start.is_some()
379 && nested_fence_end.is_none()
380 && trimmed.starts_with("```")
381 && trimmed.trim_start_matches('`').trim().is_empty()
382 {
383 nested_fence_end = Some(i);
384 }
385
386 if trimmed.starts_with(&active_fence_type.to_string().repeat(3)) {
388 let count = trimmed.chars().take_while(|&c| c == active_fence_type).count();
389 let remaining = if trimmed.len() > count {
390 trimmed[count..].trim()
391 } else {
392 ""
393 };
394
395 let is_valid_closing_fence =
401 count >= block_fence_length && remaining.is_empty() && indent <= block_indent;
402
403 let is_nested_closing = nested_fence_end.is_some() && i == nested_fence_end.unwrap();
406
407 if is_valid_closing_fence && !is_nested_closing {
409 in_block = false;
410 in_markdown_block = false;
411 }
412 }
413 }
414 }
415
416 self.code_block_lines = Some(code_block_lines);
417 }
418}
419
420pub fn calculate_single_line_range(line: usize, start_col: usize, length: usize) -> (usize, usize, usize, usize) {
422 (line, start_col, line, start_col + length)
423}
424
425pub fn calculate_line_range(line: usize, line_content: &str) -> (usize, usize, usize, usize) {
427 let trimmed_len = line_content.trim_end().len();
428 (line, 1, line, trimmed_len + 1)
429}
430
431pub fn calculate_match_range(
437 line: usize,
438 line_content: &str,
439 match_start: usize,
440 match_len: usize,
441) -> (usize, usize, usize, usize) {
442 let line_len = line_content.len();
444 if match_start > line_len {
445 let char_count = line_content.chars().count();
447 return (line, char_count + 1, line, char_count + 1);
448 }
449
450 let safe_match_start = find_char_boundary(line_content, match_start);
452 let safe_match_end_byte = find_char_boundary(line_content, (match_start + match_len).min(line_len));
453
454 let char_start = byte_to_char_count(line_content, safe_match_start);
456 let char_len = if safe_match_end_byte > safe_match_start {
457 line_content[safe_match_start..safe_match_end_byte].chars().count()
459 } else {
460 0
461 };
462 (line, char_start, line, char_start + char_len)
463}
464
465pub fn calculate_trailing_range(line: usize, line_content: &str, content_end: usize) -> (usize, usize, usize, usize) {
471 let safe_content_end = find_char_boundary(line_content, content_end);
473 let char_content_end = byte_to_char_count(line_content, safe_content_end);
474 let line_char_len = line_content.chars().count() + 1;
475 (line, char_content_end, line, line_char_len)
476}
477
478pub fn calculate_heading_range(line: usize, line_content: &str) -> (usize, usize, usize, usize) {
480 calculate_line_range(line, line_content)
481}
482
483pub fn calculate_emphasis_range(
489 line: usize,
490 line_content: &str,
491 start_pos: usize,
492 end_pos: usize,
493) -> (usize, usize, usize, usize) {
494 let safe_start_pos = find_char_boundary(line_content, start_pos);
496 let safe_end_pos = find_char_boundary(line_content, end_pos);
497 let char_start = byte_to_char_count(line_content, safe_start_pos);
498 let char_end = byte_to_char_count(line_content, safe_end_pos);
499 (line, char_start, line, char_end)
500}
501
502pub fn calculate_html_tag_range(
504 line: usize,
505 line_content: &str,
506 tag_start: usize,
507 tag_len: usize,
508) -> (usize, usize, usize, usize) {
509 calculate_match_range(line, line_content, tag_start, tag_len)
510}
511
512pub fn calculate_url_range(
514 line: usize,
515 line_content: &str,
516 url_start: usize,
517 url_len: usize,
518) -> (usize, usize, usize, usize) {
519 calculate_match_range(line, line_content, url_start, url_len)
520}
521
522pub fn calculate_list_marker_range(
524 line: usize,
525 line_content: &str,
526 marker_start: usize,
527 marker_len: usize,
528) -> (usize, usize, usize, usize) {
529 calculate_match_range(line, line_content, marker_start, marker_len)
530}
531
532pub fn calculate_excess_range(line: usize, line_content: &str, limit: usize) -> (usize, usize, usize, usize) {
534 let char_limit = std::cmp::min(limit, line_content.chars().count());
535 let line_char_len = line_content.chars().count() + 1;
536 (line, char_limit + 1, line, line_char_len)
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542
543 #[test]
544 fn test_single_line_range() {
545 let (start_line, start_col, end_line, end_col) = calculate_single_line_range(5, 10, 3);
546 assert_eq!(start_line, 5);
547 assert_eq!(start_col, 10);
548 assert_eq!(end_line, 5);
549 assert_eq!(end_col, 13);
550 }
551
552 #[test]
553 fn test_line_range() {
554 let content = "# This is a heading ";
555 let (start_line, start_col, end_line, end_col) = calculate_line_range(1, content);
556 assert_eq!(start_line, 1);
557 assert_eq!(start_col, 1);
558 assert_eq!(end_line, 1);
559 assert_eq!(end_col, 20); }
561
562 #[test]
563 fn test_match_range() {
564 let content = "Text <div>content</div> more";
565 let tag_start = 5; let tag_len = 5; let (start_line, start_col, end_line, end_col) = calculate_match_range(1, content, tag_start, tag_len);
568 assert_eq!(start_line, 1);
569 assert_eq!(start_col, 6); assert_eq!(end_line, 1);
571 assert_eq!(end_col, 11); }
573
574 #[test]
575 fn test_trailing_range() {
576 let content = "Text content "; let content_end = 12; let (start_line, start_col, end_line, end_col) = calculate_trailing_range(1, content, content_end);
579 assert_eq!(start_line, 1);
580 assert_eq!(start_col, 13); assert_eq!(end_line, 1);
582 assert_eq!(end_col, 16); }
584
585 #[test]
586 fn test_excess_range() {
587 let content = "This line is too long for the limit";
588 let limit = 20;
589 let (start_line, start_col, end_line, end_col) = calculate_excess_range(1, content, limit);
590 assert_eq!(start_line, 1);
591 assert_eq!(start_col, 21); assert_eq!(end_line, 1);
593 assert_eq!(end_col, 36); }
595
596 #[test]
597 fn test_whole_line_range() {
598 let content = "Line 1\nLine 2\nLine 3";
599 let line_index = LineIndex::new(content);
600
601 let range = line_index.whole_line_range(1);
603 assert_eq!(range, 0..7); let range = line_index.whole_line_range(2);
607 assert_eq!(range, 7..14); let range = line_index.whole_line_range(3);
611 assert_eq!(range, 14..20); }
613
614 #[test]
615 fn test_line_content_range() {
616 let content = "Line 1\nLine 2\nLine 3";
617 let line_index = LineIndex::new(content);
618
619 let range = line_index.line_content_range(1);
621 assert_eq!(range, 0..6); let range = line_index.line_content_range(2);
625 assert_eq!(range, 7..13); let range = line_index.line_content_range(3);
629 assert_eq!(range, 14..20); }
631
632 #[test]
633 fn test_line_text_range() {
634 let content = "Hello world\nAnother line";
635 let line_index = LineIndex::new(content);
636
637 let range = line_index.line_text_range(1, 1, 5); assert_eq!(range, 0..4);
640
641 let range = line_index.line_text_range(2, 1, 7); assert_eq!(range, 12..18);
644
645 let range = line_index.line_text_range(1, 1, 100); assert_eq!(range, 0..11); }
649
650 #[test]
651 fn test_calculate_match_range_bounds_checking() {
652 let line_content = "] not a link [";
654 let (line, start_col, end_line, end_col) = calculate_match_range(121, line_content, 57, 10);
655 assert_eq!(line, 121);
656 assert_eq!(start_col, 15); assert_eq!(end_line, 121);
658 assert_eq!(end_col, 15); let line_content = "short";
662 let (line, start_col, end_line, end_col) = calculate_match_range(1, line_content, 2, 10);
663 assert_eq!(line, 1);
664 assert_eq!(start_col, 3); assert_eq!(end_line, 1);
666 assert_eq!(end_col, 6); let line_content = "normal text here";
670 let (line, start_col, end_line, end_col) = calculate_match_range(5, line_content, 7, 4);
671 assert_eq!(line, 5);
672 assert_eq!(start_col, 8); assert_eq!(end_line, 5);
674 assert_eq!(end_col, 12); let line_content = "test line";
678 let (line, start_col, end_line, end_col) = calculate_match_range(10, line_content, 5, 0);
679 assert_eq!(line, 10);
680 assert_eq!(start_col, 6); assert_eq!(end_line, 10);
682 assert_eq!(end_col, 6); }
684
685 #[test]
690 fn test_issue_154_korean_character_boundary() {
691 let line_content = "- 2023 년 초 이후 주가 상승 +1,000% (10 배 상승) ";
694
695 let (line, start_col, end_line, end_col) = calculate_match_range(1, line_content, 19, 1);
698
699 assert!(start_col > 0);
701 assert_eq!(line, 1);
702 assert_eq!(end_line, 1);
703 assert!(end_col >= start_col);
704 }
705
706 #[test]
707 fn test_calculate_match_range_korean() {
708 let line_content = "안녕하세요";
711 let (line, start_col, end_line, end_col) = calculate_match_range(1, line_content, 3, 3);
713 assert_eq!(line, 1);
714 assert_eq!(start_col, 2); assert_eq!(end_line, 1);
716 assert_eq!(end_col, 3); let (line, start_col, end_line, _end_col) = calculate_match_range(1, line_content, 4, 3);
720 assert_eq!(line, 1);
721 assert_eq!(start_col, 2); assert_eq!(end_line, 1);
723 }
724
725 #[test]
726 fn test_calculate_match_range_chinese() {
727 let line_content = "你好世界";
730 let (line, start_col, end_line, end_col) = calculate_match_range(1, line_content, 6, 3);
732 assert_eq!(line, 1);
733 assert_eq!(start_col, 3); assert_eq!(end_line, 1);
735 assert_eq!(end_col, 4); }
737
738 #[test]
739 fn test_calculate_match_range_japanese() {
740 let line_content = "こんにちは";
743 let (line, start_col, end_line, end_col) = calculate_match_range(1, line_content, 9, 3);
745 assert_eq!(line, 1);
746 assert_eq!(start_col, 4); assert_eq!(end_line, 1);
748 assert_eq!(end_col, 5); }
750
751 #[test]
752 fn test_calculate_match_range_mixed_unicode() {
753 let line_content = "Hello 世界";
758
759 let (line, start_col, end_line, end_col) = calculate_match_range(1, line_content, 5, 1);
761 assert_eq!(line, 1);
762 assert_eq!(start_col, 6); assert_eq!(end_line, 1);
764 assert_eq!(end_col, 7); let (line, start_col, end_line, end_col) = calculate_match_range(1, line_content, 6, 3);
768 assert_eq!(line, 1);
769 assert_eq!(start_col, 7); assert_eq!(end_line, 1);
771 assert_eq!(end_col, 8); }
773
774 #[test]
775 fn test_calculate_trailing_range_korean() {
776 let line_content = "안녕하세요 ";
778 let (line, start_col, end_line, end_col) = calculate_trailing_range(1, line_content, 15);
780 assert_eq!(line, 1);
781 assert!(start_col > 0);
782 assert_eq!(end_line, 1);
783 assert!(end_col > start_col);
784 }
785
786 #[test]
787 fn test_calculate_emphasis_range_chinese() {
788 let line_content = "这是**重要**的";
790 let (line, start_col, end_line, end_col) = calculate_emphasis_range(1, line_content, 6, 12);
792 assert_eq!(line, 1);
793 assert!(start_col > 0);
794 assert_eq!(end_line, 1);
795 assert!(end_col > start_col);
796 }
797
798 #[test]
799 fn test_line_col_to_byte_range_korean() {
800 let content = "안녕하세요\nWorld";
802 let line_index = LineIndex::new(content);
803
804 let range = line_index.line_col_to_byte_range(1, 1);
806 assert_eq!(range, 0..0);
807
808 let range = line_index.line_col_to_byte_range(1, 2);
810 assert_eq!(range, 3..3); let range = line_index.line_col_to_byte_range(1, 3);
814 assert_eq!(range, 6..6); }
816
817 #[test]
818 fn test_line_col_to_byte_range_with_length_chinese() {
819 let content = "你好世界\nTest";
821 let line_index = LineIndex::new(content);
822
823 let range = line_index.line_col_to_byte_range_with_length(1, 1, 2);
825 assert_eq!(range, 0..6); let range = line_index.line_col_to_byte_range_with_length(1, 2, 1);
829 assert_eq!(range, 3..6); }
831
832 #[test]
833 fn test_line_text_range_japanese() {
834 let content = "こんにちは\nHello";
836 let line_index = LineIndex::new(content);
837
838 let range = line_index.line_text_range(1, 2, 4);
840 assert_eq!(range, 3..9); }
842
843 #[test]
844 fn test_find_char_boundary_edge_cases() {
845 let s = "안녕";
847
848 assert_eq!(find_char_boundary(s, 0), 0);
850
851 assert_eq!(find_char_boundary(s, 1), 0);
853
854 assert_eq!(find_char_boundary(s, 2), 0);
856
857 assert_eq!(find_char_boundary(s, 3), 3);
859
860 assert_eq!(find_char_boundary(s, 4), 3);
862
863 assert_eq!(find_char_boundary(s, 100), s.len());
865 }
866
867 #[test]
868 fn test_byte_to_char_count_unicode() {
869 let s = "안녕하세요";
871
872 assert_eq!(byte_to_char_count(s, 0), 1);
874
875 assert_eq!(byte_to_char_count(s, 3), 2);
877
878 assert_eq!(byte_to_char_count(s, 6), 3);
880
881 assert_eq!(byte_to_char_count(s, 9), 4);
883
884 assert_eq!(byte_to_char_count(s, 12), 5);
886
887 assert_eq!(byte_to_char_count(s, 15), 6);
889 }
890
891 #[test]
892 fn test_all_range_functions_with_emoji() {
893 let line_content = "Hello 🎉 World 🌍";
895
896 let (line, start_col, end_line, end_col) = calculate_match_range(1, line_content, 6, 4);
898 assert_eq!(line, 1);
899 assert!(start_col > 0);
900 assert_eq!(end_line, 1);
901 assert!(end_col > start_col);
902
903 let (line, start_col, end_line, end_col) = calculate_trailing_range(1, line_content, 12);
905 assert_eq!(line, 1);
906 assert!(start_col > 0);
907 assert_eq!(end_line, 1);
908 assert!(end_col > start_col);
909
910 let (line, start_col, end_line, end_col) = calculate_emphasis_range(1, line_content, 0, 5);
912 assert_eq!(line, 1);
913 assert_eq!(start_col, 1);
914 assert_eq!(end_line, 1);
915 assert!(end_col > start_col);
916 }
917}