yerba 0.7.2-arm-linux-gnu → 0.7.3-arm-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b4a184098ccba1de65340ff7812ed2e99dd2c4e040f5091cb17a4a1301e55b3
4
- data.tar.gz: 923fae627e8e773b8192e82d4860bfa2c2ea28db670c32a67a9125b61485c53f
3
+ metadata.gz: ecfeca9c43c3708322928fe623215d02773d8c226c3971466e8a81473d4a2cdf
4
+ data.tar.gz: 9e379bf439a5ea897b1589e423979425633265754af31baf47d71cbcd1852e87
5
5
  SHA512:
6
- metadata.gz: 65dcad11c2c0dc109ab61757c64b15724190a9d587e8b1bddc0a5f76d2cae1a7da84a52f2b785bbd2a9cbf22d8e42f07174b4e5383c0e4a54d3e8275aa6efb47
7
- data.tar.gz: 5aa046c0d8357e3acbc732faacb7950a3db6e233780ed3a653f89405c8eef64998a918b72711a125a485dad5448ad8251f38b9b643250459d428d1b9d6df3428
6
+ metadata.gz: bb13da59bd4c3ffb1e40fb1cf3733b9690c4d3329bf0a328b022a0888fe39bdcc63afc7bdd7acfbdd0c86964b0dfd00bfc2cd22aaf162bcb367872a5101f63f5
7
+ data.tar.gz: b3bf1896c57e023e56a9da53a4a5c7fe6bc3e22e8ab1eb8b0e8fb5c990766c22ea8bf4d72f1b750db20fbdb575bc8d67f85d3d64729a1e33a00003a58f0ac9e4
Binary file
Binary file
Binary file
Binary file
Binary file
data/lib/yerba/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yerba
4
- VERSION = "0.7.2"
4
+ VERSION = "0.7.3"
5
5
  end
data/rust/Cargo.lock CHANGED
@@ -1772,7 +1772,7 @@ dependencies = [
1772
1772
 
1773
1773
  [[package]]
1774
1774
  name = "yerba"
1775
- version = "0.7.2"
1775
+ version = "0.7.3"
1776
1776
  dependencies = [
1777
1777
  "cbindgen",
1778
1778
  "clap",
data/rust/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "yerba"
3
- version = "0.7.2"
3
+ version = "0.7.3"
4
4
  edition = "2021"
5
5
  authors = ["Marco Roth <marco.roth@intergga.ch>"]
6
6
  description = "YAML Editing and Refactoring with Better Accuracy"
@@ -11,7 +11,7 @@ impl Document {
11
11
  }
12
12
 
13
13
  pub fn filter_with_selectors(&self, dot_path: &str, condition: &str) -> Vec<(yaml_serde::Value, String, usize)> {
14
- let source = self.root.text().to_string();
14
+ let source = self.source_text();
15
15
 
16
16
  self
17
17
  .navigate_all_compact(dot_path)
@@ -19,7 +19,7 @@ impl Document {
19
19
  .filter(|node| self.evaluate_condition_on_node(node, condition))
20
20
  .map(|node| {
21
21
  let offset: usize = node.text_range().start().into();
22
- let line = source[..offset].matches('\n').count() + 1;
22
+ let line = line_at(&source, offset);
23
23
 
24
24
  (node_to_yaml_value(node), super::get::node_selector(node), line)
25
25
  })
@@ -52,7 +52,7 @@ impl Document {
52
52
  }
53
53
 
54
54
  for node in &target_nodes {
55
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
55
+ if let Some(sequence) = find_block_sequence(node) {
56
56
  for entry in sequence.entries() {
57
57
  if let Some(text) = entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())) {
58
58
  if text == right {
@@ -67,7 +67,7 @@ impl Document {
67
67
  }
68
68
  "not_contains" => {
69
69
  for node in &target_nodes {
70
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
70
+ if let Some(sequence) = find_block_sequence(node) {
71
71
  for entry in sequence.entries() {
72
72
  if let Some(text) = entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())) {
73
73
  if text == right {
@@ -13,10 +13,7 @@ impl Document {
13
13
  let (parent_path, source_key) = source_path.rsplit_once('.').unwrap_or(("", source_path));
14
14
  let parent_node = self.navigate(parent_path)?;
15
15
 
16
- let map = parent_node
17
- .descendants()
18
- .find_map(BlockMap::cast)
19
- .ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
16
+ let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
20
17
 
21
18
  let entry = find_entry_by_key(&map, source_key).ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
22
19
  let key_node = entry.key().ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
@@ -46,10 +43,7 @@ impl Document {
46
43
 
47
44
  if parent_path.is_empty() && !has_wildcard {
48
45
  let parent_node = self.navigate(&parent_path)?;
49
- let map = parent_node
50
- .descendants()
51
- .find_map(BlockMap::cast)
52
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
46
+ let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
53
47
 
54
48
  let entry = find_entry_by_key(&map, last_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
55
49
 
@@ -67,10 +61,7 @@ impl Document {
67
61
  }
68
62
 
69
63
  if parent_nodes.len() == 1 && !has_wildcard {
70
- let map = parent_nodes[0]
71
- .descendants()
72
- .find_map(BlockMap::cast)
73
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
64
+ let map = find_block_map(&parent_nodes[0]).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
74
65
 
75
66
  let entry = find_entry_by_key(&map, last_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
76
67
 
@@ -80,24 +71,16 @@ impl Document {
80
71
  let mut ranges: Vec<TextRange> = Vec::new();
81
72
 
82
73
  for parent_node in &parent_nodes {
83
- if let Some(map) = parent_node.descendants().find_map(BlockMap::cast) {
74
+ if let Some(map) = find_block_map(parent_node) {
84
75
  if let Some(entry) = find_entry_by_key(&map, last_key) {
85
76
  ranges.push(removal_range(entry.syntax()));
86
77
  }
87
78
  }
88
79
  }
89
80
 
90
- if ranges.is_empty() {
91
- return Ok(());
92
- }
93
-
94
- ranges.reverse();
95
-
96
- for range in ranges {
97
- self.apply_edit(range, "")?;
98
- }
81
+ let edits = ranges.into_iter().map(|range| (range, String::new())).collect();
99
82
 
100
- Ok(())
83
+ self.apply_edits(edits)
101
84
  }
102
85
 
103
86
  crate::selector::SelectorSegment::Index(index) => self.remove_at(&parent_path, *index),
@@ -108,10 +91,7 @@ impl Document {
108
91
  pub fn remove(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
109
92
  let current_node = self.navigate(dot_path)?;
110
93
 
111
- let sequence = current_node
112
- .descendants()
113
- .find_map(BlockSeq::cast)
114
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
94
+ let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
115
95
 
116
96
  let target_entry = sequence
117
97
  .entries()
@@ -132,10 +112,7 @@ impl Document {
132
112
 
133
113
  let current_node = self.navigate(dot_path)?;
134
114
 
135
- let sequence = current_node
136
- .descendants()
137
- .find_map(BlockSeq::cast)
138
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
115
+ let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
139
116
 
140
117
  let entries: Vec<_> = sequence.entries().collect();
141
118
 
@@ -83,7 +83,7 @@ impl Document {
83
83
  }
84
84
 
85
85
  pub fn get_all_located(&self, dot_path: &str) -> Vec<LocatedNode> {
86
- let source = self.root.text().to_string();
86
+ let source = self.source_text();
87
87
  let file_path = self.path.as_ref().map(|p| p.to_string_lossy().to_string());
88
88
 
89
89
  self
@@ -91,7 +91,7 @@ impl Document {
91
91
  .iter()
92
92
  .map(|node| {
93
93
  let offset: usize = node.text_range().start().into();
94
- let line = source[..offset].matches('\n').count() + 1;
94
+ let line = line_at(&source, offset);
95
95
  let selector = node_selector(node);
96
96
  let is_scalar = !node
97
97
  .descendants()
@@ -108,13 +108,8 @@ impl Document {
108
108
  let node_type = if is_scalar {
109
109
  "scalar"
110
110
  } else {
111
- let map_pos = node.descendants().find_map(BlockMap::cast).map(|m| m.syntax().text_range().start());
112
- let seq_pos = node.descendants().find_map(BlockSeq::cast).map(|s| s.syntax().text_range().start());
113
-
114
- match (map_pos, seq_pos) {
115
- (Some(m), Some(s)) if s < m => "sequence",
116
- (Some(_), _) => "map",
117
- (None, Some(_)) => "sequence",
111
+ match first_collection(node) {
112
+ Some(FirstCollection::Sequence(_)) => "sequence",
118
113
  _ => "map",
119
114
  }
120
115
  };
@@ -142,7 +137,7 @@ impl Document {
142
137
 
143
138
  pub fn get_node_info(&self, dot_path: &str) -> NodeInfo {
144
139
  let selector = crate::selector::Selector::parse(dot_path);
145
- let source = self.root.text().to_string();
140
+ let source = self.source_text();
146
141
 
147
142
  let (location, key_name, key_location) = self.resolve_location(dot_path, &source);
148
143
 
@@ -364,7 +359,7 @@ impl Document {
364
359
  self.navigate(parent_path).ok()?
365
360
  };
366
361
 
367
- let map = parent_node.descendants().find_map(BlockMap::cast)?;
362
+ let map = find_block_map(&parent_node)?;
368
363
 
369
364
  find_entry_by_key(&map, last_key).map(|entry| entry.syntax().clone())
370
365
  }
@@ -375,7 +370,7 @@ impl Document {
375
370
  Err(_) => return Vec::new(),
376
371
  };
377
372
 
378
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
373
+ let sequence = match find_block_sequence(&current_node) {
379
374
  Some(sequence) => sequence,
380
375
  None => return Vec::new(),
381
376
  };
@@ -403,17 +398,16 @@ impl Document {
403
398
  pub fn get_sequence_indent(&self, dot_path: &str) -> Option<&'static str> {
404
399
  let current_node = self.navigate(dot_path).ok()?;
405
400
 
406
- let sequence = current_node.descendants().find_map(BlockSeq::cast)?;
401
+ let sequence = find_block_sequence(&current_node)?;
407
402
  let first_entry = sequence.entries().next()?;
408
403
 
409
404
  let entry_indent = preceding_whitespace_indent(first_entry.syntax());
410
405
 
411
406
  let entry_node = current_node.ancestors().find(|ancestor| ancestor.kind() == SyntaxKind::BLOCK_MAP_ENTRY)?;
412
407
 
413
- let source = self.root.text().to_string();
408
+ let source = self.source_text();
414
409
  let entry_start: usize = entry_node.text_range().start().into();
415
- let line_start = source[..entry_start].rfind('\n').map(|position| position + 1).unwrap_or(0);
416
- let key_indent = &source[line_start..entry_start];
410
+ let key_indent = &source[line_start_at(&source, entry_start)..entry_start];
417
411
 
418
412
  if entry_indent.len() > key_indent.len() {
419
413
  Some("indented")
@@ -60,10 +60,7 @@ impl Document {
60
60
  let quote_style = self.detect_sequence_quote_style(dot_path);
61
61
  let current_node = self.navigate(dot_path)?;
62
62
 
63
- let sequence = current_node
64
- .descendants()
65
- .find_map(BlockSeq::cast)
66
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
63
+ let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
67
64
 
68
65
  let entries: Vec<_> = sequence.entries().collect();
69
66
 
@@ -81,38 +78,7 @@ impl Document {
81
78
 
82
79
  for json_value in json_values {
83
80
  let yaml_text = crate::yaml_writer::json_to_yaml_text(json_value, &quote_style, 0);
84
-
85
- let new_item = if yaml_text.contains('\n') {
86
- let item_indent = format!("{} ", indent);
87
- let lines: Vec<&str> = yaml_text.split('\n').collect();
88
-
89
- let min_indent = lines
90
- .iter()
91
- .skip(1)
92
- .filter(|line| !line.trim().is_empty())
93
- .map(|line| line.len() - line.trim_start().len())
94
- .min()
95
- .unwrap_or(0);
96
-
97
- let indented: Vec<String> = lines
98
- .iter()
99
- .enumerate()
100
- .map(|(index, line)| {
101
- if index == 0 {
102
- line.to_string()
103
- } else if line.trim().is_empty() {
104
- String::new()
105
- } else {
106
- let relative = &line[min_indent..];
107
- format!("{}{}", item_indent, relative)
108
- }
109
- })
110
- .collect();
111
-
112
- format!("- {}", indented.join("\n"))
113
- } else {
114
- format!("- {}", yaml_text)
115
- };
81
+ let new_item = Self::format_sequence_item(&yaml_text, &indent);
116
82
 
117
83
  new_text.push_str(&format!("\n{}{}", indent, new_item));
118
84
  }
@@ -126,7 +92,7 @@ impl Document {
126
92
  Self::validate_path(dot_path)?;
127
93
 
128
94
  if let Ok(current_node) = self.navigate(dot_path) {
129
- if current_node.descendants().find_map(BlockSeq::cast).is_some()
95
+ if find_block_sequence(&current_node).is_some()
130
96
  || matches!(self.get_value(dot_path).as_ref(), Some(yaml_serde::Value::Sequence(sequence)) if sequence.is_empty())
131
97
  {
132
98
  return self.insert_sequence_item(dot_path, value, position);
@@ -152,7 +118,7 @@ impl Document {
152
118
  None => continue,
153
119
  };
154
120
 
155
- let map = match node.descendants().find_map(BlockMap::cast) {
121
+ let map = match find_block_map(&node) {
156
122
  Some(map) => map,
157
123
  None => {
158
124
  let has_empty_flow_map = node
@@ -186,42 +152,13 @@ impl Document {
186
152
 
187
153
  let start_col = {
188
154
  let offset: usize = first_entry.syntax().text_range().start().into();
189
- let source = self.to_string();
190
- let before = &source[..offset];
155
+ let source = self.source_text();
191
156
 
192
- offset - before.rfind('\n').map(|p| p + 1).unwrap_or(0)
157
+ column_at(&source, offset)
193
158
  };
194
159
 
195
160
  let indent = " ".repeat(start_col);
196
-
197
- let is_block_value = value.contains('\n') || value.starts_with("- ");
198
- let new_entry_text = if is_block_value {
199
- let value_indent = format!("{} ", indent);
200
- let lines: Vec<&str> = value.lines().collect();
201
-
202
- let min_indent = lines
203
- .iter()
204
- .filter(|line| !line.trim().is_empty())
205
- .map(|line| line.len() - line.trim_start().len())
206
- .min()
207
- .unwrap_or(0);
208
-
209
- let indented_lines: Vec<String> = lines
210
- .iter()
211
- .map(|line| {
212
- if line.trim().is_empty() {
213
- String::new()
214
- } else {
215
- let relative = &line[min_indent..];
216
- format!("{}{}", value_indent, relative)
217
- }
218
- })
219
- .collect();
220
-
221
- format!("{}:\n{}", key, indented_lines.join("\n"))
222
- } else {
223
- format!("{}: {}", key, value)
224
- };
161
+ let new_entry_text = Self::format_map_entry(key, value, &indent);
225
162
 
226
163
  match &position {
227
164
  InsertPosition::After(target_key) => {
@@ -265,7 +202,7 @@ impl Document {
265
202
  fn insert_sequence_item(&mut self, dot_path: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
266
203
  let current_node = self.navigate(dot_path)?;
267
204
 
268
- let Some(sequence) = current_node.descendants().find_map(BlockSeq::cast) else {
205
+ let Some(sequence) = find_block_sequence(&current_node) else {
269
206
  if matches!(self.get_value(dot_path).as_ref(), Some(yaml_serde::Value::Sequence(sequence)) if sequence.is_empty()) {
270
207
  return self.replace_empty_inline_sequence(dot_path, value);
271
208
  }
@@ -285,37 +222,7 @@ impl Document {
285
222
  .map(|entry| preceding_whitespace_indent(entry.syntax()))
286
223
  .unwrap_or_default();
287
224
 
288
- let new_item = if value.contains('\n') {
289
- let item_indent = format!("{} ", indent);
290
- let lines: Vec<&str> = value.split('\n').collect();
291
-
292
- let min_indent = lines
293
- .iter()
294
- .skip(1)
295
- .filter(|line| !line.trim().is_empty())
296
- .map(|line| line.len() - line.trim_start().len())
297
- .min()
298
- .unwrap_or(0);
299
-
300
- let indented: Vec<String> = lines
301
- .iter()
302
- .enumerate()
303
- .map(|(index, line)| {
304
- if index == 0 {
305
- line.to_string()
306
- } else if line.trim().is_empty() {
307
- String::new()
308
- } else {
309
- let relative = &line[min_indent..];
310
- format!("{}{}", item_indent, relative)
311
- }
312
- })
313
- .collect();
314
-
315
- format!("- {}", indented.join("\n"))
316
- } else {
317
- format!("- {}", value)
318
- };
225
+ let new_item = Self::format_sequence_item(value, &indent);
319
226
 
320
227
  match position {
321
228
  InsertPosition::Last => {
@@ -413,7 +320,7 @@ impl Document {
413
320
  fn insert_map_key(&mut self, dot_path: &str, key: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
414
321
  let current_node = self.navigate(dot_path)?;
415
322
 
416
- let map = match current_node.descendants().find_map(BlockMap::cast) {
323
+ let map = match find_block_map(&current_node) {
417
324
  Some(map) => map,
418
325
  None => {
419
326
  let flow_map = current_node.descendants().find(|descendant| descendant.kind() == SyntaxKind::FLOW_MAP);
@@ -449,34 +356,7 @@ impl Document {
449
356
  .map(|entry| preceding_whitespace_indent(entry.syntax()))
450
357
  .unwrap_or_default();
451
358
 
452
- let is_block_value = value.contains('\n') || value.starts_with("- ");
453
- let new_entry_text = if is_block_value {
454
- let value_indent = format!("{} ", indent);
455
- let lines: Vec<&str> = value.lines().collect();
456
-
457
- let min_indent = lines
458
- .iter()
459
- .filter(|line| !line.trim().is_empty())
460
- .map(|line| line.len() - line.trim_start().len())
461
- .min()
462
- .unwrap_or(0);
463
-
464
- let indented_lines: Vec<String> = lines
465
- .iter()
466
- .map(|line| {
467
- if line.trim().is_empty() {
468
- String::new()
469
- } else {
470
- let relative = &line[min_indent..];
471
- format!("{}{}", value_indent, relative)
472
- }
473
- })
474
- .collect();
475
-
476
- format!("{}:\n{}", key, indented_lines.join("\n"))
477
- } else {
478
- format!("{}: {}", key, value)
479
- };
359
+ let new_entry_text = Self::format_map_entry(key, value, &indent);
480
360
 
481
361
  match position {
482
362
  InsertPosition::Last => {
@@ -561,7 +441,7 @@ impl Document {
561
441
 
562
442
  let range = flow_map.text_range();
563
443
 
564
- let source = self.to_string();
444
+ let source = self.source_text();
565
445
  let start: usize = range.start().into();
566
446
  let before = &source[..start];
567
447
  let trimmed_length = before.trim_end_matches([' ', '\n']).len();
@@ -575,10 +455,7 @@ impl Document {
575
455
  let (parent_path, map_key) = dot_path.rsplit_once('.').unwrap_or(("", dot_path));
576
456
  let parent_node = self.navigate(parent_path)?;
577
457
 
578
- let map = parent_node
579
- .descendants()
580
- .find_map(BlockMap::cast)
581
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
458
+ let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
582
459
 
583
460
  let entry = find_entry_by_key(&map, map_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
584
461
  let entry_indent = preceding_whitespace_indent(entry.syntax());
@@ -601,14 +478,14 @@ impl Document {
601
478
  fn replace_empty_inline_sequence(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
602
479
  if dot_path.is_empty() {
603
480
  let current_node = self.navigate(dot_path)?;
604
- let flow_seq = current_node
481
+ let flow_sequence = current_node
605
482
  .descendants()
606
483
  .find(|descendant| descendant.kind() == SyntaxKind::FLOW_SEQ)
607
484
  .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
608
485
 
609
486
  let new_item = Self::format_sequence_item(value, "");
610
- let range = flow_seq.text_range();
611
- let source = self.to_string();
487
+ let range = flow_sequence.text_range();
488
+ let source = self.source_text();
612
489
  let start: usize = range.start().into();
613
490
  let before = &source[..start];
614
491
 
@@ -624,10 +501,7 @@ impl Document {
624
501
  let (parent_path, key) = dot_path.rsplit_once('.').unwrap_or(("", dot_path));
625
502
  let parent_node = self.navigate(parent_path)?;
626
503
 
627
- let map = parent_node
628
- .descendants()
629
- .find_map(BlockMap::cast)
630
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
504
+ let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
631
505
 
632
506
  let entry = find_entry_by_key(&map, key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
633
507
  let item_indent = format!("{} ", preceding_whitespace_indent(entry.syntax()));
@@ -656,7 +530,7 @@ impl Document {
656
530
  }
657
531
 
658
532
  fn trailing_inline_comment(&self, node: &SyntaxNode) -> Option<(String, rowan::TextSize)> {
659
- let source = self.root.text().to_string();
533
+ let source = self.source_text();
660
534
  let start: usize = node.text_range().end().into();
661
535
  let rest = &source[start..];
662
536
  let line_end = rest.find('\n').unwrap_or(rest.len());
@@ -670,6 +544,38 @@ impl Document {
670
544
  Some((trailing[comment_start..].to_string(), rowan::TextSize::from((start + line_end) as u32)))
671
545
  }
672
546
 
547
+ fn format_map_entry(key: &str, value: &str, indent: &str) -> String {
548
+ let is_block_value = value.contains('\n') || value.starts_with("- ");
549
+
550
+ if !is_block_value {
551
+ return format!("{}: {}", key, value);
552
+ }
553
+
554
+ let value_indent = format!("{} ", indent);
555
+ let lines: Vec<&str> = value.lines().collect();
556
+
557
+ let min_indent = lines
558
+ .iter()
559
+ .filter(|line| !line.trim().is_empty())
560
+ .map(|line| line.len() - line.trim_start().len())
561
+ .min()
562
+ .unwrap_or(0);
563
+
564
+ let indented_lines: Vec<String> = lines
565
+ .iter()
566
+ .map(|line| {
567
+ if line.trim().is_empty() {
568
+ String::new()
569
+ } else {
570
+ let relative = &line[min_indent..];
571
+ format!("{}{}", value_indent, relative)
572
+ }
573
+ })
574
+ .collect();
575
+
576
+ format!("{}:\n{}", key, indented_lines.join("\n"))
577
+ }
578
+
673
579
  fn format_sequence_item(value: &str, indent: &str) -> String {
674
580
  if value.contains('\n') {
675
581
  let item_indent = format!("{} ", indent);