Skip to content

Commit bc59f43

Browse files
committed
feat: support indent with exclude
1 parent 5d6c747 commit bc59f43

File tree

3 files changed

+59
-24
lines changed

3 files changed

+59
-24
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "string_wizard"
3-
version = "0.0.10"
3+
version = "0.0.11"
44
edition = "2021"
55
license = "MIT"
66
description = "manipulate string like wizards"

src/magic_string/indent.rs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::{CowStr, MagicString};
1+
use rustc_hash::FxHashSet;
2+
3+
use crate::{CowStr, MagicString, TextSize};
24

35
fn guess_indent_str(source: &str) -> Option<String> {
46
let mut tabbed_count = 0;
@@ -36,6 +38,7 @@ fn guess_indent_str(source: &str) -> Option<String> {
3638
pub struct IndentOptions<'a> {
3739
/// MagicString will guess the `indent_str`` from lines of the source if passed `None`.
3840
pub indent_str: Option<&'a str>,
41+
pub exclude: Vec<TextSize>,
3942
}
4043

4144
impl<'text> MagicString<'text> {
@@ -48,13 +51,17 @@ impl<'text> MagicString<'text> {
4851
}
4952

5053
pub fn indent(&mut self) -> &mut Self {
51-
self.indent_with(IndentOptions { indent_str: None })
54+
self.indent_with(IndentOptions {
55+
indent_str: None,
56+
..Default::default()
57+
})
5258
}
5359

5460
/// Shortcut for `indent_with(IndentOptions { indent_str: Some(indent_str), ..Default::default() })`
5561
pub fn indent_str(&mut self, indent_str: &str) -> &mut Self {
5662
self.indent_with(IndentOptions {
5763
indent_str: Some(indent_str),
64+
..Default::default()
5865
})
5966
}
6067

@@ -116,33 +123,49 @@ impl<'text> MagicString<'text> {
116123
for intro_frag in self.intro.iter_mut() {
117124
indent_frag(intro_frag, &pattern, &mut indent_replacer)
118125
}
119-
126+
let is_excluded = {
127+
let mut is_excluded = FxHashSet::default();
128+
let mut exclude = opts.exclude;
129+
exclude.sort();
130+
exclude.windows(2).for_each(|s| {
131+
for i in s[0]..s[1] {
132+
is_excluded.insert(i);
133+
}
134+
});
135+
is_excluded
136+
};
120137
let mut next_chunk_id = Some(self.first_chunk_idx);
121-
138+
let mut char_index = 0u32;
122139
while let Some(chunk_idx) = next_chunk_id {
123140
// Make sure the `next_chunk_id` is updated before we split the chunk. Otherwise, we
124141
// might process the same chunk twice.
125142
next_chunk_id = self.chunks[chunk_idx].next;
126143
if let Some(edited_content) = self.chunks[chunk_idx].edited_content.as_mut() {
127-
indent_frag(edited_content, &pattern, &mut indent_replacer);
144+
if !is_excluded.contains(&char_index) {
145+
indent_frag(edited_content, &pattern, &mut indent_replacer);
146+
}
128147
} else {
129148
let chunk = &self.chunks[chunk_idx];
130149
let mut line_starts = vec![];
131-
let mut char_index = chunk.start();
150+
char_index = chunk.start();
151+
let chunk_end = chunk.end();
132152
for char in chunk.span.text(&self.source).chars() {
133153
debug_assert!(self.source.is_char_boundary(char_index as usize));
134-
if char == '\n' {
135-
indent_replacer.should_indent_next_char = true;
136-
} else if char != '\r' && indent_replacer.should_indent_next_char {
137-
indent_replacer.should_indent_next_char = false;
138-
debug_assert!(!line_starts.contains(&char_index));
139-
line_starts.push(char_index);
154+
if !is_excluded.contains(&char_index) {
155+
if char == '\n' {
156+
indent_replacer.should_indent_next_char = true;
157+
} else if char != '\r' && indent_replacer.should_indent_next_char {
158+
indent_replacer.should_indent_next_char = false;
159+
debug_assert!(!line_starts.contains(&char_index));
160+
line_starts.push(char_index);
161+
}
140162
}
141163
char_index += char.len_utf8() as u32;
142164
}
143165
for line_start in line_starts {
144166
self.prepend_right(line_start, indent_replacer.indent_str.clone());
145167
}
168+
char_index = chunk_end;
146169
}
147170
}
148171

tests/magic_string.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ mod relocate {
268268
}
269269

270270
mod indent {
271+
use string_wizard::IndentOptions;
272+
271273
use super::*;
272274

273275
#[test]
@@ -276,7 +278,7 @@ mod indent {
276278
s.indent();
277279
assert_eq!(s.to_string(), "\tabc\n\tdef\n\tghi\n\tjkl")
278280
}
279-
281+
280282
#[test]
281283
fn should_indent_content_with_a_single_tab_character_by_default2() {
282284
let mut s = MagicString::new("");
@@ -324,12 +326,18 @@ mod indent {
324326
assert_eq!(s.to_string(), "abc\ndef\nghi\njkl");
325327
}
326328
#[test]
327-
#[ignore = "TODO: support exclude"]
328329
fn should_prevent_excluded_characters_from_being_indented() {
329-
// should indent content using the empty string if specified (i.e. noop)
330-
let _s = MagicString::new("abc\ndef\nghi\njkl");
331-
// s.indent_with(IndentOptions { indent_str: " ", exclude: vec![7, 15], });
332-
330+
let mut s = MagicString::new("abc\ndef\nghi\njkl");
331+
s.indent_with(IndentOptions {
332+
indent_str: Some(" "),
333+
exclude: vec![7, 15],
334+
});
335+
assert_eq!(s.to_string(), " abc\n def\nghi\njkl");
336+
s.indent_with(IndentOptions {
337+
indent_str: Some(">>"),
338+
exclude: vec![7, 15],
339+
});
340+
assert_eq!(s.to_string(), ">> abc\n>> def\nghi\njkl");
333341
}
334342

335343
#[test]
@@ -347,9 +355,15 @@ mod indent {
347355
// should indent content using the empty string if specified (i.e. noop)
348356
let mut s = MagicString::new("\r\n\r\nabc\r\ndef\r\n\r\nghi\r\njkl");
349357
s.indent();
350-
assert_eq!(s.to_string(), "\r\n\r\n\tabc\r\n\tdef\r\n\r\n\tghi\r\n\tjkl");
358+
assert_eq!(
359+
s.to_string(),
360+
"\r\n\r\n\tabc\r\n\tdef\r\n\r\n\tghi\r\n\tjkl"
361+
);
351362
s.indent();
352-
assert_eq!(s.to_string(), "\r\n\r\n\t\tabc\r\n\t\tdef\r\n\r\n\t\tghi\r\n\t\tjkl");
363+
assert_eq!(
364+
s.to_string(),
365+
"\r\n\r\n\t\tabc\r\n\t\tdef\r\n\r\n\t\tghi\r\n\t\tjkl"
366+
);
353367
}
354368

355369
#[test]
@@ -359,7 +373,7 @@ mod indent {
359373
s.indent();
360374
assert_eq!(s.to_string(), "\tvar foo = 1;");
361375
}
362-
376+
363377
#[test]
364378
fn should_not_indent_patches_in_the_middle_of_a_line() {
365379
let mut s = MagicString::new("class Foo extends Bar {}");
@@ -368,8 +382,6 @@ mod indent {
368382
s.indent();
369383
assert_eq!(s.to_string(), "\tclass Foo extends Baz {}");
370384
}
371-
372-
373385
}
374386

375387
mod misc {

0 commit comments

Comments
 (0)