Skip to content

Commit 739d6a7

Browse files
authored
fix: rope ends_with (#158)
1 parent ecf2f30 commit 739d6a7

6 files changed

+152
-39
lines changed

src/helpers.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ pub fn get_generated_source_info<'a, S>(source: S) -> GeneratedInfo
240240
where
241241
S: SourceText<'a>,
242242
{
243-
let (generated_line, generated_column) = if source.ends_with("\n") {
243+
let (generated_line, generated_column) = if source.ends_with('\n') {
244244
(source.split_into_lines().count() + 1, 0)
245245
} else {
246246
let mut line_count = 0;
@@ -288,7 +288,7 @@ where
288288
last_line = Some(l);
289289
}
290290
if let Some(last_line) =
291-
last_line.filter(|last_line| !last_line.ends_with("\n"))
291+
last_line.filter(|last_line| !last_line.ends_with('\n'))
292292
{
293293
GeneratedInfo {
294294
generated_line: line - 1,
@@ -443,7 +443,7 @@ where
443443
}
444444
let last_line =
445445
&line_with_indices_list[line_with_indices_list.len() - 1].line;
446-
let last_new_line = last_line.ends_with("\n");
446+
let last_new_line = last_line.ends_with('\n');
447447
let final_line: u32 = if last_new_line {
448448
line_with_indices_list.len() + 1
449449
} else {
@@ -686,7 +686,7 @@ where
686686
current_generated_line += 1;
687687
}
688688
let last_line = &lines[lines.len() - 1];
689-
let last_new_line = last_line.ends_with("\n");
689+
let last_new_line = last_line.ends_with('\n');
690690
let final_line = if last_new_line {
691691
lines.len() + 1
692692
} else {
@@ -1249,7 +1249,7 @@ pub trait SourceText<'a>: Default + Clone + ToString {
12491249
fn split_into_lines(&self) -> impl Iterator<Item = Self>;
12501250

12511251
/// Checks if the text ends with the given string.
1252-
fn ends_with(&self, value: &str) -> bool;
1252+
fn ends_with(&self, value: char) -> bool;
12531253

12541254
/// Returns an iterator over the char indices in the text.
12551255
fn char_indices(&self) -> impl Iterator<Item = (usize, char)>;
@@ -1285,7 +1285,7 @@ impl<'a> SourceText<'a> for Rope<'a> {
12851285
}
12861286

12871287
#[inline]
1288-
fn ends_with(&self, value: &str) -> bool {
1288+
fn ends_with(&self, value: char) -> bool {
12891289
(*self).ends_with(value)
12901290
}
12911291

@@ -1327,7 +1327,7 @@ impl<'a> SourceText<'a> for &'a str {
13271327
}
13281328

13291329
#[inline]
1330-
fn ends_with(&self, value: &str) -> bool {
1330+
fn ends_with(&self, value: char) -> bool {
13311331
(*self).ends_with(value)
13321332
}
13331333

src/original_source.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ impl StreamChunks for OriginalSource {
218218
last_line = Some(l);
219219
}
220220
if let Some(last_line) =
221-
last_line.filter(|last_line| !last_line.ends_with("\n"))
221+
last_line.filter(|last_line| !last_line.ends_with('\n'))
222222
{
223223
GeneratedInfo {
224224
generated_line: line - 1,

src/replace_source.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
431431
// Skip over the whole chunk
432432
if replacement_end >= end_pos {
433433
let line = mapping.generated_line as i64 + generated_line_offset;
434-
if chunk.ends_with("\n") {
434+
if chunk.ends_with('\n') {
435435
generated_line_offset -= 1;
436436
if generated_column_offset_line == line {
437437
// undo exiting corrections form the current line
@@ -567,7 +567,7 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
567567
// Only the first chunk has name assigned
568568
replacement_name_index = None;
569569

570-
if m == lines.len() - 1 && !content_line.ends_with("\n") {
570+
if m == lines.len() - 1 && !content_line.ends_with('\n') {
571571
if generated_column_offset_line == line {
572572
generated_column_offset += content_line.len() as i64;
573573
} else {
@@ -607,7 +607,7 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
607607
.is_some_and(|replacement_end| replacement_end >= end_pos)
608608
{
609609
let line = mapping.generated_line as i64 + generated_line_offset;
610-
if chunk.ends_with("\n") {
610+
if chunk.ends_with('\n') {
611611
generated_line_offset -= 1;
612612
if generated_column_offset_line == line {
613613
// undo exiting corrections form the current line
@@ -731,7 +731,7 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
731731
},
732732
);
733733

734-
if m == matches.len() - 1 && !content_line.ends_with("\n") {
734+
if m == matches.len() - 1 && !content_line.ends_with('\n') {
735735
if generated_column_offset_line == line {
736736
generated_column_offset += content_line.len() as i64;
737737
} else {

src/rope.rs

+46-18
Original file line numberDiff line numberDiff line change
@@ -59,33 +59,42 @@ impl<'a> Rope<'a> {
5959
pub fn append(&mut self, value: Rope<'a>) {
6060
match (&mut self.repr, value.repr) {
6161
(Repr::Light(s), Repr::Light(other)) => {
62+
if other.is_empty() {
63+
return;
64+
}
6265
let raw = Vec::from_iter([(*s, 0), (other, s.len())]);
6366
self.repr = Repr::Full(Rc::new(raw));
6467
}
6568
(Repr::Full(s), Repr::Full(other)) => {
66-
if !other.is_empty() {
67-
let mut len = s
68-
.last()
69-
.map_or(0, |(chunk, start_pos)| *start_pos + chunk.len());
69+
if other.is_empty() {
70+
return;
71+
}
72+
let mut len = s
73+
.last()
74+
.map_or(0, |(chunk, start_pos)| *start_pos + chunk.len());
7075

71-
let cur = Rc::make_mut(s);
72-
cur.reserve_exact(other.len());
76+
let cur = Rc::make_mut(s);
77+
cur.reserve_exact(other.len());
7378

74-
for &(chunk, _) in other.iter() {
75-
cur.push((chunk, len));
76-
len += chunk.len();
77-
}
79+
for &(chunk, _) in other.iter() {
80+
cur.push((chunk, len));
81+
len += chunk.len();
7882
}
7983
}
8084
(Repr::Full(s), Repr::Light(other)) => {
81-
if !other.is_empty() {
82-
let len = s
83-
.last()
84-
.map_or(0, |(chunk, start_pos)| *start_pos + chunk.len());
85-
Rc::make_mut(s).push((other, len));
85+
if other.is_empty() {
86+
return;
8687
}
88+
let len = s
89+
.last()
90+
.map_or(0, |(chunk, start_pos)| *start_pos + chunk.len());
91+
Rc::make_mut(s).push((other, len));
8792
}
8893
(Repr::Light(s), Repr::Full(other)) => {
94+
if s.is_empty() {
95+
self.repr = Repr::Full(other.clone());
96+
return;
97+
}
8998
let mut raw = Vec::with_capacity(other.len() + 1);
9099
raw.push((*s, 0));
91100
let mut len = s.len();
@@ -228,7 +237,7 @@ impl<'a> Rope<'a> {
228237

229238
/// Returns whether the rope ends with the given string.
230239
#[inline]
231-
pub fn ends_with(&self, value: &str) -> bool {
240+
pub fn ends_with(&self, value: char) -> bool {
232241
match &self.repr {
233242
Repr::Light(s) => s.ends_with(value),
234243
Repr::Full(data) => {
@@ -975,10 +984,13 @@ impl<'a> FromIterator<&'a str> for Rope<'a> {
975984
let mut len = 0;
976985
let raw = iter
977986
.into_iter()
978-
.map(|chunk| {
987+
.filter_map(|chunk| {
988+
if chunk.is_empty() {
989+
return None;
990+
}
979991
let cur = (chunk, len);
980992
len += chunk.len();
981-
cur
993+
Some(cur)
982994
})
983995
.collect::<Vec<_>>();
984996

@@ -1337,4 +1349,20 @@ mod tests {
13371349
assert!(!rope.starts_with(&Rope::from_iter(vec!["a", "b", "c", "d"])));
13381350
assert!(!rope.starts_with(&Rope::from_iter(vec!["b", "c"])));
13391351
}
1352+
1353+
#[test]
1354+
fn ends_with() {
1355+
let rope = Rope::from("abc\n");
1356+
assert!(rope.ends_with('\n'));
1357+
1358+
let mut rope = Rope::from("abc\n");
1359+
rope.append("".into());
1360+
assert!(rope.ends_with('\n'));
1361+
1362+
let rope = Rope::from_iter(["abc\n", ""]);
1363+
assert!(rope.ends_with('\n'));
1364+
1365+
let rope = Rope::from_iter(["abc", "\n"]);
1366+
assert!(rope.ends_with('\n'));
1367+
}
13401368
}

src/source.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ fn is_all_empty(val: &Arc<[String]>) -> bool {
194194
}
195195

196196
/// The source map created by [Source::map].
197-
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
197+
#[derive(Clone, PartialEq, Eq, Serialize)]
198198
pub struct SourceMap {
199199
version: u8,
200200
#[serde(skip_serializing_if = "Option::is_none")]
@@ -210,6 +210,23 @@ pub struct SourceMap {
210210
debug_id: Option<Arc<str>>,
211211
}
212212

213+
impl std::fmt::Debug for SourceMap {
214+
fn fmt(
215+
&self,
216+
f: &mut std::fmt::Formatter<'_>,
217+
) -> std::result::Result<(), std::fmt::Error> {
218+
let indent = f.width().unwrap_or(0);
219+
let indent_str = format!("{:indent$}", "", indent = indent);
220+
221+
write!(
222+
f,
223+
"{indent_str}SourceMap::from_json({:?}).unwrap()",
224+
self.clone().to_json().unwrap()
225+
)?;
226+
227+
Ok(())
228+
}
229+
}
213230
impl Hash for SourceMap {
214231
fn hash<H: Hasher>(&self, state: &mut H) {
215232
self.file.hash(state);

src/source_map_source.rs

+76-8
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,41 @@ impl std::fmt::Debug for SourceMapSource {
143143
&self,
144144
f: &mut std::fmt::Formatter<'_>,
145145
) -> Result<(), std::fmt::Error> {
146-
f.debug_struct("SourceMapSource")
147-
.field("name", &self.name)
148-
.field("value", &self.value)
149-
.field("source_map", &self.source_map)
150-
.field("original_source", &self.original_source)
151-
.field("inner_source_map", &self.inner_source_map)
152-
.field("remove_original_source", &self.remove_original_source)
153-
.finish()
146+
let indent = f.width().unwrap_or(0);
147+
let indent_str = format!("{:indent$}", "", indent = indent);
148+
149+
writeln!(
150+
f,
151+
"{indent_str}SourceMapSource::new(SourceMapSourceOptions {{"
152+
)?;
153+
writeln!(f, "{indent_str} value: {:?},", self.value)?;
154+
writeln!(f, "{indent_str} name: {:?},", self.name)?;
155+
writeln!(f, "{indent_str} source_map: {:?},", self.source_map)?;
156+
match &self.original_source {
157+
Some(original_source) => {
158+
writeln!(
159+
f,
160+
"{indent_str} original_source: Some({:?}.to_string()),",
161+
original_source
162+
)?;
163+
}
164+
None => {
165+
writeln!(f, "{indent_str} original_source: None,")?;
166+
}
167+
}
168+
writeln!(
169+
f,
170+
"{indent_str} inner_source_map: {:?},",
171+
self.inner_source_map
172+
)?;
173+
writeln!(
174+
f,
175+
"{indent_str} remove_original_source: {:?},",
176+
self.remove_original_source
177+
)?;
178+
write!(f, "{indent_str}}}).boxed()")?;
179+
180+
Ok(())
154181
}
155182
}
156183

@@ -730,4 +757,45 @@ mod tests {
730757
.unwrap()
731758
);
732759
}
760+
761+
#[test]
762+
fn debug() {
763+
let source = SourceMapSource::new(SourceMapSourceOptions {
764+
value: "hello world",
765+
name: "hello.txt",
766+
source_map: SourceMap::from_json(
767+
r#"{
768+
"version": 3,
769+
"sources": ["hello.txt"],
770+
"mappings": "AAAA,MAAG"
771+
}"#,
772+
)
773+
.unwrap(),
774+
original_source: Some("你好 世界".to_string()),
775+
inner_source_map: Some(
776+
SourceMap::from_json(
777+
r#"{
778+
"version": 3,
779+
"sources": ["hello world.txt"],
780+
"mappings": "AAAA,EAAE",
781+
"sourcesContent": ["你好✋世界"]
782+
}"#,
783+
)
784+
.unwrap(),
785+
),
786+
remove_original_source: false,
787+
});
788+
789+
assert_eq!(
790+
format!("{:?}", source),
791+
r#"SourceMapSource::new(SourceMapSourceOptions {
792+
value: "hello world",
793+
name: "hello.txt",
794+
source_map: SourceMap::from_json("{\"version\":3,\"sources\":[\"hello.txt\"],\"names\":[],\"mappings\":\"AAAA,MAAG\"}").unwrap(),
795+
original_source: Some("你好 世界".to_string()),
796+
inner_source_map: Some(SourceMap::from_json("{\"version\":3,\"sources\":[\"hello world.txt\"],\"sourcesContent\":[\"你好✋世界\"],\"names\":[],\"mappings\":\"AAAA,EAAE\"}").unwrap()),
797+
remove_original_source: false,
798+
}).boxed()"#
799+
);
800+
}
733801
}

0 commit comments

Comments
 (0)