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
use crate::*; use std::cmp::Ordering; #[derive(Clone, Debug, Eq)] pub struct Location { pub uri: URI, pub offset: usize, pub line: usize, pub character: usize, } impl Location { pub fn at_offset(source: &Arc<Source>, offset: usize) -> Location { let chars: Vec<u16> = source.code.encode_utf16().collect(); let code_before = &chars[..offset]; let lines: Vec<_> = code_before.split(|c| *c == '\n' as u16).collect(); Location { uri: source.uri.clone(), offset, line: lines.len(), character: lines.last().unwrap().len() + 1, } } pub fn at_end_of(source: &Arc<Source>) -> Location { Self::at_offset(source, source.code.len()) } pub fn at_position(source: &Arc<Source>, line: usize, character: usize) -> Option<Location> { let mut chars: Vec<u16> = source.code.encode_utf16().collect(); let mut lines: Vec<&mut [u16]> = chars.split_mut(|c| *c == '\n' as u16).collect(); if lines.len() < line { warn!( "Tried to get position on line {} but the source had {} lines.", line, lines.len() ); return None; } let lines_before = &mut lines[..line]; if lines_before.len() == 0 { return Some(Location { uri: source.uri.clone(), offset: 0, line: 1, character: 1, }); } let last_line_before = &mut lines_before[lines_before.len() - 1]; if last_line_before.len() < character - 1 { warn!( "Tried to get position on character {} but the line had {} characters.", character, last_line_before.len() ); return None; } let mut new_last_line: Vec<_> = last_line_before[..character - 1].iter().cloned().collect(); *last_line_before = new_last_line.as_mut_slice(); let mut offset = 0; for line in lines_before.iter() { for _ in line.iter() { offset += 1; } offset += 1; } if offset > 0 { offset -= 1; } Some(Location { uri: source.uri.clone(), offset, line, character, }) } } impl fmt::Display for Location { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}:{}", self.uri, self.line, self.character) } } impl PartialEq for Location { fn eq(&self, other: &Self) -> bool { self.uri == other.uri && self.offset == other.offset } } impl Ord for Location { fn cmp(&self, other: &Self) -> Ordering { self.offset.cmp(&other.offset) } } impl PartialOrd for Location { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.offset.cmp(&other.offset)) } } #[cfg(test)] mod tests { use super::*; #[test] fn start_location_in_source() { let source = Source::test("hello"); let location = Location::at_offset(&source, 0); assert_eq!(location.uri, source.uri); assert_eq!(location.offset, 0); assert_eq!(location.line, 1); assert_eq!(location.character, 1); } #[test] fn location_in_source() { let source = Source::test("hello"); let location = Location::at_offset(&source, 3); assert_eq!(location.uri, source.uri); assert_eq!(location.offset, 3); assert_eq!(location.line, 1); assert_eq!(location.character, 4); } #[test] fn multiline() { let source = Source::test("hello\nthere"); let location = Location::at_offset(&source, 6); assert_eq!(location.uri, source.uri); assert_eq!(location.offset, 6); assert_eq!(location.line, 2); assert_eq!(location.character, 1); } #[test] fn last_location() { let source = Source::test("hello\nthere"); let location = Location::at_offset(&source, 11); assert_eq!(location.uri, source.uri); assert_eq!(location.offset, 11); assert_eq!(location.line, 2); assert_eq!(location.character, 6); } }