yerba 0.9.2-arm-linux-gnu → 0.10.1-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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +53 -17
  3. data/exe/arm-linux-gnu/yerba +0 -0
  4. data/ext/yerba/include/yerba.h +58 -35
  5. data/ext/yerba/yerba.c +281 -187
  6. data/lib/yerba/3.2/yerba.so +0 -0
  7. data/lib/yerba/3.3/yerba.so +0 -0
  8. data/lib/yerba/3.4/yerba.so +0 -0
  9. data/lib/yerba/4.0/yerba.so +0 -0
  10. data/lib/yerba/collection.rb +4 -4
  11. data/lib/yerba/document.rb +134 -0
  12. data/lib/yerba/formatting.rb +1 -0
  13. data/lib/yerba/location.rb +8 -1
  14. data/lib/yerba/map.rb +16 -6
  15. data/lib/yerba/node.rb +41 -0
  16. data/lib/yerba/query_result.rb +33 -10
  17. data/lib/yerba/scalar.rb +29 -0
  18. data/lib/yerba/selector.rb +3 -1
  19. data/lib/yerba/sequence.rb +38 -33
  20. data/lib/yerba/version.rb +1 -1
  21. data/rust/Cargo.lock +1 -1
  22. data/rust/Cargo.toml +1 -1
  23. data/rust/src/commands/get.rs +12 -12
  24. data/rust/src/commands/init.rs +8 -8
  25. data/rust/src/commands/insert.rs +4 -18
  26. data/rust/src/commands/location.rs +2 -0
  27. data/rust/src/commands/mod.rs +34 -6
  28. data/rust/src/commands/move_item.rs +1 -1
  29. data/rust/src/commands/move_key.rs +8 -8
  30. data/rust/src/commands/rename.rs +1 -1
  31. data/rust/src/commands/selectors.rs +1 -1
  32. data/rust/src/commands/set.rs +14 -6
  33. data/rust/src/commands/sort.rs +5 -1
  34. data/rust/src/document/condition.rs +25 -25
  35. data/rust/src/document/delete.rs +57 -43
  36. data/rust/src/document/get.rs +91 -82
  37. data/rust/src/document/insert.rs +77 -77
  38. data/rust/src/document/mod.rs +202 -76
  39. data/rust/src/document/nodes.rs +162 -0
  40. data/rust/src/document/schema.rs +8 -8
  41. data/rust/src/document/set.rs +49 -26
  42. data/rust/src/document/sort.rs +105 -60
  43. data/rust/src/document/style.rs +50 -49
  44. data/rust/src/document/unique.rs +5 -5
  45. data/rust/src/error.rs +8 -4
  46. data/rust/src/ffi.rs +234 -107
  47. data/rust/src/json.rs +62 -2
  48. data/rust/src/lib.rs +4 -2
  49. data/rust/src/main.rs +2 -1
  50. data/rust/src/selector.rs +48 -7
  51. data/rust/src/syntax.rs +449 -56
  52. data/rust/src/validation.rs +5 -3
  53. data/rust/src/yerbafile.rs +168 -94
  54. metadata +3 -2
@@ -2,10 +2,10 @@ use super::*;
2
2
 
3
3
  impl Document {
4
4
  pub fn validate_schema(&self, schema: &serde_json::Value, items: bool, selector: Option<&str>) -> Vec<crate::schema::ValidationError> {
5
- let path = selector.unwrap_or("");
6
- let has_wildcard = crate::selector::Selector::parse(path).has_wildcard();
5
+ let selector = selector.unwrap_or("");
6
+ let has_wildcard = crate::selector::Selector::parse(selector).has_wildcard();
7
7
 
8
- let value = match self.get_value(path) {
8
+ let value = match self.get_value(selector) {
9
9
  Some(value) => crate::json::yaml_to_json(&value),
10
10
  None => return vec![],
11
11
  };
@@ -62,12 +62,12 @@ fn json_pointer_to_selector(pointer: &str) -> String {
62
62
  segment.to_string()
63
63
  }
64
64
  })
65
- .fold(String::new(), |mut path, segment| {
66
- if !path.is_empty() && !segment.starts_with('[') {
67
- path.push('.');
65
+ .fold(String::new(), |mut selector, segment| {
66
+ if !selector.is_empty() && !segment.starts_with('[') {
67
+ selector.push('.');
68
68
  }
69
69
 
70
- path.push_str(&segment);
71
- path
70
+ selector.push_str(&segment);
71
+ selector
72
72
  })
73
73
  }
@@ -1,7 +1,7 @@
1
1
  use super::*;
2
2
 
3
3
  fn is_plain_writable(value: &str) -> bool {
4
- crate::syntax::is_plain_safe(value) && !crate::syntax::is_yaml_non_string(value)
4
+ crate::syntax::is_plain_safe(value) && !crate::syntax::is_yaml_non_string(value) && !crate::syntax::resolves_to_non_string_in_psych(value)
5
5
  }
6
6
 
7
7
  fn scalar_replacement_text(value: &str, kind: SyntaxKind) -> String {
@@ -112,23 +112,23 @@ fn replacement_text(span: &ValueSpan, value: &str) -> String {
112
112
  }
113
113
 
114
114
  impl Document {
115
- pub fn set(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
116
- self.set_with(dot_path, value, false)
115
+ pub fn set(&mut self, selector: &str, value: &str) -> Result<(), YerbaError> {
116
+ self.set_with(selector, value, false)
117
117
  }
118
118
 
119
- pub fn set_all(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
120
- self.set_all_with(dot_path, value, false)
119
+ pub fn set_all(&mut self, selector: &str, value: &str) -> Result<(), YerbaError> {
120
+ self.set_all_with(selector, value, false)
121
121
  }
122
122
 
123
- pub fn set_all_plain(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
124
- self.set_all_with(dot_path, value, true)
123
+ pub fn set_all_plain(&mut self, selector: &str, value: &str) -> Result<(), YerbaError> {
124
+ self.set_all_with(selector, value, true)
125
125
  }
126
126
 
127
- fn set_all_with(&mut self, dot_path: &str, value: &str, plain: bool) -> Result<(), YerbaError> {
128
- let nodes = self.navigate_all_compact(dot_path);
127
+ fn set_all_with(&mut self, selector: &str, value: &str, plain: bool) -> Result<(), YerbaError> {
128
+ let nodes = self.navigate_all_compact(selector);
129
129
 
130
130
  if nodes.is_empty() {
131
- return Err(YerbaError::SelectorNotFound(dot_path.to_string()));
131
+ return Err(YerbaError::SelectorNotFound(selector.to_string()));
132
132
  }
133
133
 
134
134
  let source = self.source_text();
@@ -137,13 +137,21 @@ impl Document {
137
137
  self.apply_edits(edits)
138
138
  }
139
139
 
140
- pub fn set_where(&mut self, container_path: &str, relative_path: &str, value: &str, condition: &str, all: bool, plain: bool) -> Result<usize, YerbaError> {
140
+ pub fn set_where(
141
+ &mut self,
142
+ container_selector: &str,
143
+ relative_selector: &str,
144
+ value: &str,
145
+ condition: &str,
146
+ all: bool,
147
+ plain: bool,
148
+ ) -> Result<usize, YerbaError> {
141
149
  validate_item_condition(condition)?;
142
150
 
143
- let items = self.navigate_all_compact(container_path);
151
+ let items = self.navigate_all_compact(container_selector);
144
152
 
145
153
  if items.is_empty() {
146
- return Err(YerbaError::SelectorNotFound(container_path.to_string()));
154
+ return Err(YerbaError::SelectorNotFound(container_selector.to_string()));
147
155
  }
148
156
 
149
157
  let source = self.source_text();
@@ -154,7 +162,7 @@ impl Document {
154
162
  continue;
155
163
  }
156
164
 
157
- targets.extend(navigate_from_node(item, relative_path));
165
+ targets.extend(navigate_from_node(item, relative_selector));
158
166
  }
159
167
 
160
168
  if targets.is_empty() {
@@ -162,10 +170,10 @@ impl Document {
162
170
  }
163
171
 
164
172
  if !all && targets.len() > 1 {
165
- let selector = if container_path.is_empty() {
166
- relative_path.to_string()
173
+ let selector = if container_selector.is_empty() {
174
+ relative_selector.to_string()
167
175
  } else {
168
- format!("{}.{}", container_path, relative_path)
176
+ format!("{}.{}", container_selector, relative_selector)
169
177
  };
170
178
 
171
179
  return Err(YerbaError::AmbiguousSelector(selector, targets.len()));
@@ -179,9 +187,9 @@ impl Document {
179
187
  Ok(count)
180
188
  }
181
189
 
182
- pub fn set_scalar_style(&mut self, dot_path: &str, style: &QuoteStyle) -> Result<(), YerbaError> {
183
- let current_node = self.navigate(dot_path)?;
184
- let scalar_token = find_scalar_token(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
190
+ pub fn set_scalar_style(&mut self, selector: &str, style: &QuoteStyle) -> Result<(), YerbaError> {
191
+ let current_node = self.navigate(selector)?;
192
+ let scalar_token = find_scalar_token(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
185
193
 
186
194
  let current_kind = scalar_token.kind();
187
195
  let target_kind = style.to_syntax_kind();
@@ -200,12 +208,15 @@ impl Document {
200
208
  self.replace_token(&scalar_token, &new_text)
201
209
  }
202
210
 
203
- pub fn set_plain(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
204
- self.set_with(dot_path, value, true)
211
+ pub fn set_plain(&mut self, selector: &str, value: &str) -> Result<(), YerbaError> {
212
+ self.set_with(selector, value, true)
205
213
  }
206
214
 
207
- fn set_with(&mut self, dot_path: &str, value: &str, plain: bool) -> Result<(), YerbaError> {
208
- let current_node = self.navigate(dot_path)?;
215
+ fn set_with(&mut self, selector: &str, value: &str, plain: bool) -> Result<(), YerbaError> {
216
+ let current_node = match self.navigate(selector) {
217
+ Ok(node) => node,
218
+ Err(error) => return self.set_valueless_entry(selector, value, plain).unwrap_or(Err(error)),
219
+ };
209
220
 
210
221
  if let Some(block_scalar) = current_node.descendants().find(|node| node.kind() == SyntaxKind::BLOCK_SCALAR) {
211
222
  if plain {
@@ -219,13 +230,13 @@ impl Document {
219
230
  }
220
231
 
221
232
  if holds_collection(&current_node) {
222
- let span = value_span(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
233
+ let span = value_span(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
223
234
  let replacement = if plain { value.to_string() } else { crate::syntax::quote_scalar(value) };
224
235
 
225
236
  return self.apply_edit(span.range, &replacement_text(&span, &replacement));
226
237
  }
227
238
 
228
- let scalar_token = find_scalar_token(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
239
+ let scalar_token = find_scalar_token(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
229
240
 
230
241
  if plain {
231
242
  return self.replace_token(&scalar_token, value);
@@ -236,6 +247,18 @@ impl Document {
236
247
  self.replace_token(&scalar_token, &new_text)
237
248
  }
238
249
 
250
+ fn set_valueless_entry(&mut self, selector: &str, value: &str, plain: bool) -> Option<Result<(), YerbaError>> {
251
+ let entry = self.find_entry(selector)?;
252
+ let colon = entry.children_with_tokens().find(|element| element.kind() == SyntaxKind::COLON)?;
253
+ let text = if plain {
254
+ value.to_string()
255
+ } else {
256
+ scalar_replacement_text(value, SyntaxKind::PLAIN_SCALAR)
257
+ };
258
+
259
+ Some(self.apply_edit(TextRange::empty(colon.text_range().end()), &format!(" {}", text)))
260
+ }
261
+
239
262
  fn block_scalar_replacement(source: &str, block_scalar: &SyntaxNode, value: &str) -> String {
240
263
  if value.is_empty() {
241
264
  return "\"\"".to_string();
@@ -1,38 +1,38 @@
1
1
  use super::*;
2
2
 
3
3
  impl Document {
4
- pub fn move_item(&mut self, dot_path: &str, from: usize, to: usize) -> Result<(), YerbaError> {
4
+ pub fn move_item(&mut self, selector: &str, from: usize, to: usize) -> Result<(), YerbaError> {
5
5
  if from == to {
6
6
  return Ok(());
7
7
  }
8
8
 
9
- let current_node = self.navigate(dot_path)?;
9
+ let current_node = self.navigate(selector)?;
10
10
 
11
- let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
11
+ let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(selector.to_string()))?;
12
12
 
13
13
  let entries: Vec<_> = sequence.entries().collect();
14
14
 
15
15
  self.reorder_entries(sequence.syntax(), &entries, from, to)
16
16
  }
17
17
 
18
- pub fn move_key(&mut self, dot_path: &str, from: usize, to: usize) -> Result<(), YerbaError> {
18
+ pub fn move_key(&mut self, selector: &str, from: usize, to: usize) -> Result<(), YerbaError> {
19
19
  if from == to {
20
20
  return Ok(());
21
21
  }
22
22
 
23
- let current_node = self.navigate(dot_path)?;
23
+ let current_node = self.navigate(selector)?;
24
24
 
25
- let map = find_block_map(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
25
+ let map = find_block_map(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
26
26
 
27
27
  let entries: Vec<_> = map.entries().collect();
28
28
 
29
29
  self.reorder_entries(map.syntax(), &entries, from, to)
30
30
  }
31
31
 
32
- pub fn resolve_key_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
33
- let current_node = self.navigate(dot_path)?;
32
+ pub fn resolve_key_index(&self, selector: &str, reference: &str) -> Result<usize, YerbaError> {
33
+ let current_node = self.navigate(selector)?;
34
34
 
35
- let map = find_block_map(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
35
+ let map = find_block_map(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
36
36
 
37
37
  if let Ok(index) = reference.parse::<usize>() {
38
38
  let length = map.entries().count();
@@ -55,13 +55,13 @@ impl Document {
55
55
  .unwrap_or(false)
56
56
  })
57
57
  .map(|(index, _entry)| index)
58
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} key '{}'", dot_path, reference)))
58
+ .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} key '{}'", selector, reference)))
59
59
  }
60
60
 
61
- pub fn resolve_sequence_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
62
- let current_node = self.navigate(dot_path)?;
61
+ pub fn resolve_sequence_index(&self, selector: &str, reference: &str) -> Result<usize, YerbaError> {
62
+ let current_node = self.navigate(selector)?;
63
63
 
64
- let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
64
+ let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(selector.to_string()))?;
65
65
 
66
66
  if let Ok(index) = reference.parse::<usize>() {
67
67
  let length = sequence.entries().count();
@@ -79,7 +79,7 @@ impl Document {
79
79
  .enumerate()
80
80
  .find(|(_index, entry)| self.evaluate_condition_on_node(entry.syntax(), reference))
81
81
  .map(|(index, _entry)| index)
82
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} condition '{}'", dot_path, reference)));
82
+ .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} condition '{}'", selector, reference)));
83
83
  }
84
84
 
85
85
  sequence
@@ -93,15 +93,15 @@ impl Document {
93
93
  .unwrap_or(false)
94
94
  })
95
95
  .map(|(index, _entry)| index)
96
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} item '{}'", dot_path, reference)))
96
+ .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} item '{}'", selector, reference)))
97
97
  }
98
98
 
99
- pub fn validate_sort_keys(&self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
100
- if let Some(sequence_path) = strip_bracket_suffix(dot_path) {
101
- return self.validate_each_sort_keys(sequence_path, key_order);
99
+ pub fn validate_sort_keys(&self, selector: &str, key_order: &[&str]) -> Result<(), YerbaError> {
100
+ if let Some(sequence_selector) = strip_bracket_suffix(selector) {
101
+ return self.validate_each_sort_keys(sequence_selector, key_order);
102
102
  }
103
103
 
104
- let current_node = match self.navigate(dot_path) {
104
+ let current_node = match self.navigate(selector) {
105
105
  Ok(node) => node,
106
106
  Err(_) => return Ok(()),
107
107
  };
@@ -124,12 +124,12 @@ impl Document {
124
124
  }
125
125
  }
126
126
 
127
- pub fn sort_keys(&mut self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
128
- if let Some(sequence_path) = strip_bracket_suffix(dot_path) {
129
- return self.sort_each_keys(sequence_path, key_order);
127
+ pub fn sort_keys(&mut self, selector: &str, key_order: &[&str]) -> Result<(), YerbaError> {
128
+ if let Some(sequence_selector) = strip_bracket_suffix(selector) {
129
+ return self.sort_each_keys(sequence_selector, key_order);
130
130
  }
131
131
 
132
- let current_node = match self.navigate(dot_path) {
132
+ let current_node = match self.navigate(selector) {
133
133
  Ok(node) => node,
134
134
  Err(_) => return Ok(()),
135
135
  };
@@ -145,14 +145,14 @@ impl Document {
145
145
  }
146
146
  }
147
147
 
148
- pub fn sort_each_keys(&mut self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
149
- let nodes = if dot_path.is_empty() {
150
- match self.navigate(dot_path) {
148
+ pub fn sort_each_keys(&mut self, selector: &str, key_order: &[&str]) -> Result<(), YerbaError> {
149
+ let nodes = if selector.is_empty() {
150
+ match self.navigate(selector) {
151
151
  Ok(node) => vec![node],
152
152
  Err(_) => return Ok(()),
153
153
  }
154
154
  } else {
155
- let found = self.navigate_all_compact(dot_path);
155
+ let found = self.navigate_all_compact(selector);
156
156
  if found.is_empty() {
157
157
  return Ok(());
158
158
  }
@@ -182,14 +182,14 @@ impl Document {
182
182
  self.apply_edits(edits)
183
183
  }
184
184
 
185
- pub fn validate_each_sort_keys(&self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
186
- let nodes = if dot_path.is_empty() {
187
- match self.navigate(dot_path) {
185
+ pub fn validate_each_sort_keys(&self, selector: &str, key_order: &[&str]) -> Result<(), YerbaError> {
186
+ let nodes = if selector.is_empty() {
187
+ match self.navigate(selector) {
188
188
  Ok(node) => vec![node],
189
189
  Err(_) => return Ok(()),
190
190
  }
191
191
  } else {
192
- let found = self.navigate_all_compact(dot_path);
192
+ let found = self.navigate_all_compact(selector);
193
193
  if found.is_empty() {
194
194
  return Ok(());
195
195
  }
@@ -224,39 +224,43 @@ impl Document {
224
224
  }
225
225
  }
226
226
 
227
- pub fn sort_items(&mut self, dot_path: &str, sort_fields: &[SortField], case_sensitive: bool) -> Result<(), YerbaError> {
228
- if dot_path.contains("[].") {
229
- return self.sort_each_items(dot_path, sort_fields, case_sensitive);
227
+ pub fn sort_items(&mut self, selector: &str, sort_fields: &[SortField], case_sensitive: bool) -> Result<(), YerbaError> {
228
+ self.sort_items_with(selector, sort_fields, case_sensitive, None)
229
+ }
230
+
231
+ pub fn sort_items_with(&mut self, selector: &str, sort_fields: &[SortField], case_sensitive: bool, missing: Option<SortMissing>) -> Result<(), YerbaError> {
232
+ if selector.contains("[].") {
233
+ return self.sort_each_items(selector, sort_fields, case_sensitive, missing);
230
234
  }
231
235
 
232
- let current_node = self.navigate(dot_path)?;
236
+ let current_node = self.navigate(selector)?;
233
237
 
234
238
  let sequence = match find_block_sequence(&current_node) {
235
239
  Some(sequence) => sequence,
236
240
  None => return Ok(()),
237
241
  };
238
242
 
239
- match sort_sequence_edit(&sequence, sort_fields, case_sensitive) {
243
+ match sort_sequence_edit(&sequence, sort_fields, case_sensitive, missing) {
240
244
  Some(edit) => self.apply_edits(vec![edit]),
241
245
  None => Ok(()),
242
246
  }
243
247
  }
244
248
 
245
- fn sort_each_items(&mut self, dot_path: &str, sort_fields: &[SortField], case_sensitive: bool) -> Result<(), YerbaError> {
246
- let (parent_path, child_path) = if let Some(last_bracket) = dot_path.rfind("[].") {
247
- (&dot_path[..last_bracket + 2], &dot_path[last_bracket + 3..])
249
+ fn sort_each_items(&mut self, selector: &str, sort_fields: &[SortField], case_sensitive: bool, missing: Option<SortMissing>) -> Result<(), YerbaError> {
250
+ let (parent_selector, child_selector) = if let Some(last_bracket) = selector.rfind("[].") {
251
+ (&selector[..last_bracket + 2], &selector[last_bracket + 3..])
248
252
  } else {
249
- (dot_path, "")
253
+ (selector, "")
250
254
  };
251
255
 
252
- let parent_nodes = self.navigate_all_compact(parent_path);
256
+ let parent_nodes = self.navigate_all_compact(parent_selector);
253
257
  let mut edits: Vec<(TextRange, String)> = Vec::new();
254
258
 
255
259
  for parent_node in &parent_nodes {
256
- let child_nodes = if child_path.is_empty() {
260
+ let child_nodes = if child_selector.is_empty() {
257
261
  vec![parent_node.clone()]
258
262
  } else {
259
- navigate_from_node(parent_node, child_path)
263
+ navigate_from_node(parent_node, child_selector)
260
264
  };
261
265
 
262
266
  for child_node in &child_nodes {
@@ -265,7 +269,7 @@ impl Document {
265
269
  None => continue,
266
270
  };
267
271
 
268
- if let Some(edit) = sort_sequence_edit(&sequence, sort_fields, case_sensitive) {
272
+ if let Some(edit) = sort_sequence_edit(&sequence, sort_fields, case_sensitive, missing) {
269
273
  edits.push(edit);
270
274
  }
271
275
  }
@@ -274,20 +278,20 @@ impl Document {
274
278
  self.apply_edits(edits)
275
279
  }
276
280
 
277
- pub fn reorder_items(&mut self, dot_path: &str, by: &str, desired_order: &[&str]) -> Result<(), YerbaError> {
281
+ pub fn reorder_items(&mut self, selector: &str, by: &str, desired_order: &[&str]) -> Result<(), YerbaError> {
278
282
  let field = by.strip_prefix('.').unwrap_or(by);
279
283
  let is_scalar = field.is_empty() || field == ".";
280
284
 
281
285
  let items_selector = if is_scalar {
282
- if dot_path.is_empty() {
286
+ if selector.is_empty() {
283
287
  "[]".to_string()
284
288
  } else {
285
- format!("{}[]", dot_path)
289
+ format!("{}[]", selector)
286
290
  }
287
- } else if dot_path.is_empty() {
291
+ } else if selector.is_empty() {
288
292
  format!("[].{}", field)
289
293
  } else {
290
- format!("{}[].{}", dot_path, field)
294
+ format!("{}[].{}", selector, field)
291
295
  };
292
296
 
293
297
  let labels: Vec<String> = match self.get_value(&items_selector) {
@@ -332,7 +336,7 @@ impl Document {
332
336
  let source = moves[target];
333
337
 
334
338
  if source != target {
335
- self.move_item(dot_path, source, target)?;
339
+ self.move_item(selector, source, target)?;
336
340
 
337
341
  for item in moves.iter_mut().skip(target + 1) {
338
342
  if *item >= target && *item < source {
@@ -398,7 +402,7 @@ fn sort_map_edit(map: &BlockMap, key_order: &[&str]) -> Option<(TextRange, Strin
398
402
  Some((range, rebuild_from_groups(&sorted_groups, &indent, false)))
399
403
  }
400
404
 
401
- fn sort_sequence_edit(sequence: &BlockSeq, sort_fields: &[SortField], case_sensitive: bool) -> Option<(TextRange, String)> {
405
+ fn sort_sequence_edit(sequence: &BlockSeq, sort_fields: &[SortField], case_sensitive: bool, missing: Option<SortMissing>) -> Option<(TextRange, String)> {
402
406
  let entries: Vec<_> = sequence.entries().collect();
403
407
 
404
408
  if entries.len() <= 1 {
@@ -415,7 +419,30 @@ fn sort_sequence_edit(sequence: &BlockSeq, sort_fields: &[SortField], case_sensi
415
419
 
416
420
  let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
417
421
 
418
- sortable.sort_by(|(values_a, _), (values_b, _)| compare_sort_values(values_a, values_b, sort_fields, case_sensitive));
422
+ let compare = |(values_a, _): &(Vec<String>, EntryGroup), (values_b, _): &(Vec<String>, EntryGroup)| {
423
+ compare_sort_values(values_a, values_b, sort_fields, case_sensitive, missing)
424
+ };
425
+
426
+ let is_absent = |(values, _): &(Vec<String>, EntryGroup)| values.iter().any(|value| value.is_empty());
427
+
428
+ match missing {
429
+ Some(SortMissing::Skip) if sortable.iter().any(is_absent) => return None,
430
+ Some(SortMissing::Keep) => {
431
+ let absent_positions: Vec<bool> = sortable.iter().map(is_absent).collect();
432
+ let (absent, mut present): (Vec<_>, Vec<_>) = sortable.into_iter().partition(is_absent);
433
+
434
+ present.sort_by(compare);
435
+
436
+ let mut present = present.into_iter();
437
+ let mut absent = absent.into_iter();
438
+
439
+ sortable = absent_positions
440
+ .iter()
441
+ .filter_map(|is_absent| if *is_absent { absent.next() } else { present.next() })
442
+ .collect();
443
+ }
444
+ _ => sortable.sort_by(compare),
445
+ }
419
446
 
420
447
  let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
421
448
 
@@ -439,17 +466,23 @@ fn entry_sort_values(entry: &yaml_parser::ast::BlockSeqEntry, sort_fields: &[Sor
439
466
  sort_fields
440
467
  .iter()
441
468
  .map(|field| {
442
- if field.path.is_empty() {
469
+ if field.selector.is_empty() {
443
470
  scalar_value()
444
471
  } else {
445
- let nodes = navigate_from_node(entry.syntax(), &field.path);
472
+ let nodes = navigate_from_node(entry.syntax(), &field.selector);
446
473
  nodes.first().and_then(extract_scalar_text).unwrap_or_default()
447
474
  }
448
475
  })
449
476
  .collect()
450
477
  }
451
478
 
452
- fn compare_sort_values(values_a: &[String], values_b: &[String], sort_fields: &[SortField], case_sensitive: bool) -> std::cmp::Ordering {
479
+ fn compare_sort_values(
480
+ values_a: &[String],
481
+ values_b: &[String],
482
+ sort_fields: &[SortField],
483
+ case_sensitive: bool,
484
+ missing: Option<SortMissing>,
485
+ ) -> std::cmp::Ordering {
453
486
  let compare = |value_a: &String, value_b: &String| {
454
487
  if case_sensitive {
455
488
  value_a.cmp(value_b)
@@ -458,7 +491,19 @@ fn compare_sort_values(values_a: &[String], values_b: &[String], sort_fields: &[
458
491
  }
459
492
  };
460
493
 
494
+ let missing_ordering = |value_a: &String, value_b: &String| match (missing, value_a.is_empty(), value_b.is_empty()) {
495
+ (Some(SortMissing::First), true, false) => Some(std::cmp::Ordering::Less),
496
+ (Some(SortMissing::First), false, true) => Some(std::cmp::Ordering::Greater),
497
+ (Some(SortMissing::Last), true, false) => Some(std::cmp::Ordering::Greater),
498
+ (Some(SortMissing::Last), false, true) => Some(std::cmp::Ordering::Less),
499
+ _ => None,
500
+ };
501
+
461
502
  for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
503
+ if let Some(ordering) = missing_ordering(&values_a[index], &values_b[index]) {
504
+ return ordering;
505
+ }
506
+
462
507
  let ordering = compare(&values_a[index], &values_b[index]);
463
508
  let ordering = if field.ascending { ordering } else { ordering.reverse() };
464
509
 
@@ -468,18 +513,18 @@ fn compare_sort_values(values_a: &[String], values_b: &[String], sort_fields: &[
468
513
  }
469
514
 
470
515
  if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
471
- return compare(&values_a[0], &values_b[0]);
516
+ return missing_ordering(&values_a[0], &values_b[0]).unwrap_or_else(|| compare(&values_a[0], &values_b[0]));
472
517
  }
473
518
 
474
519
  std::cmp::Ordering::Equal
475
520
  }
476
521
 
477
- fn strip_bracket_suffix(path: &str) -> Option<&str> {
478
- if path == "[]" {
522
+ fn strip_bracket_suffix(selector: &str) -> Option<&str> {
523
+ if selector == "[]" {
479
524
  Some("")
480
- } else if let Some(stripped) = path.strip_suffix(".[]") {
525
+ } else if let Some(stripped) = selector.strip_suffix(".[]") {
481
526
  Some(stripped)
482
527
  } else {
483
- path.strip_suffix("[]")
528
+ selector.strip_suffix("[]")
484
529
  }
485
530
  }