Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/Environment/Utility/
TextEdit.rs

1#![allow(non_snake_case, unused_variables, dead_code, unused_imports)]
2
3//! Pure text-editing utilities shared across workspace and document providers.
4//!
5//! These are side-effect-free helper functions that compute line offsets and
6//! translate `(line, character)` positions to byte offsets, matching VS Code's
7//! UTF-16 code unit counting convention for `Range`/`Position` values.
8
9/// Pre-compute the byte offset of the start of every line in `Source`.
10/// The returned vec always has at least one entry (`[0]`).
11pub fn ComputeLineOffsets(Source:&str) -> Vec<usize> {
12	let mut Offsets = Vec::with_capacity(Source.len() / 40 + 1);
13	Offsets.push(0);
14	for (Index, Byte) in Source.bytes().enumerate() {
15		if Byte == b'\n' {
16			Offsets.push(Index + 1);
17		}
18	}
19	Offsets
20}
21
22/// Resolve `(line, character)` to an absolute byte offset in `Source`.
23/// `character` is counted in **UTF-16 code units** to match VS Code's
24/// `Range`/`Position` semantics. Falls back to EOF when line/character
25/// exceeds the source length.
26pub fn LinePosToOffset(LineOffsets:&[usize], Source:&str, Line:usize, Character:usize) -> usize {
27	if Line >= LineOffsets.len() {
28		return Source.len();
29	}
30	let LineStart = LineOffsets[Line];
31	let LineEnd = if Line + 1 < LineOffsets.len() {
32		LineOffsets[Line + 1].saturating_sub(1)
33	} else {
34		Source.len()
35	};
36	let LineText = &Source[LineStart..LineEnd.min(Source.len())];
37	let mut Utf16Count:usize = 0;
38	for (ByteOffset, Char) in LineText.char_indices() {
39		if Utf16Count >= Character {
40			return LineStart + ByteOffset;
41		}
42		Utf16Count += Char.len_utf16();
43	}
44	LineStart + LineText.len()
45}
46
47/// Minimal percent-decoder for `file://` URI paths. Self-contained to avoid
48/// an extra crate dependency; handles `%XX` sequences only.
49pub fn percent_decode(Input:&str) -> String {
50	let mut Out = String::with_capacity(Input.len());
51	let mut Bytes = Input.as_bytes().iter().peekable();
52	while let Some(&Byte) = Bytes.next() {
53		if Byte == b'%' {
54			let H = Bytes.next().copied();
55			let L = Bytes.next().copied();
56			if let (Some(H), Some(L)) = (H, L) {
57				if let (Some(Hi), Some(Lo)) = (hex_digit(H), hex_digit(L)) {
58					Out.push((Hi * 16 + Lo) as char);
59					continue;
60				}
61				Out.push('%');
62				Out.push(H as char);
63				Out.push(L as char);
64				continue;
65			}
66			Out.push('%');
67		} else {
68			Out.push(Byte as char);
69		}
70	}
71	Out
72}
73
74fn hex_digit(Byte:u8) -> Option<u8> {
75	match Byte {
76		b'0'..=b'9' => Some(Byte - b'0'),
77		b'a'..=b'f' => Some(Byte - b'a' + 10),
78		b'A'..=b'F' => Some(Byte - b'A' + 10),
79		_ => None,
80	}
81}