yerba 0.7.3 → 0.7.5

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: 4305ac41ddb57b2c537679a4924163c87b715418e2e4c9d80ce9f4dc2979ebd4
4
- data.tar.gz: 425313784c600c32d9b3a054beaaf152550772a804fa40c22f96bd052852ec72
3
+ metadata.gz: d03e5cb5c88654843c8c86735f91d1e550e81282ccd1c0db8813527c4b4f9898
4
+ data.tar.gz: 805516b5ce364c09dab736e562f4a09d312d70b75ceb2c9e22a9e63fad45eb6d
5
5
  SHA512:
6
- metadata.gz: bf1fdc98836e461bd02830a07008ba86df5fd71d22d8ff478e042c5c36e89caad9cb006e5c20ac191a3c24c11fe33add62d12109e1269225464247093d549142
7
- data.tar.gz: 85fa2fac4eff2cf7662a2549b6b5dac953e4a3646e71b405774ebd39ec2200604429ea8cd11724385ae801248a7250e94c53a4cd41b9c4f126b6d93f6ec8fa71
6
+ metadata.gz: 9cb2ace3f935a023bdd938cde43010f158efc18fce052a7fe5780cf3f9362779d0bc66a463f60453586b24e0b2e6b2ed180cf7d71d2f99095b57d0eb2adc92d8
7
+ data.tar.gz: a4946b677d5b05d2741a09749e540056376dc2d2724f12bc383d722974638576bfdf022a9a02587c75b4167d9d9ae644ddcb45ed6d3f86167423c8d7263c7ba3
data/ext/yerba/yerba.c CHANGED
@@ -986,10 +986,12 @@ static VALUE collection_s_get(VALUE self, VALUE pattern, VALUE path) {
986
986
 
987
987
  VALUE line = rb_hash_aref(item, rb_str_new_cstr("line"));
988
988
  VALUE kwargs = rb_hash_new();
989
+ VALUE location = rb_hash_aref(item, rb_str_new_cstr("location"));
989
990
 
990
991
  rb_hash_aset(kwargs, ID2SYM(rb_intern("selector")), selector);
991
992
  if (!NIL_P(file_path)) rb_hash_aset(kwargs, ID2SYM(rb_intern("file_path")), file_path);
992
993
  if (!NIL_P(line)) rb_hash_aset(kwargs, ID2SYM(rb_intern("line")), line);
994
+ if (!NIL_P(location)) rb_hash_aset(kwargs, ID2SYM(rb_intern("location")), location);
993
995
 
994
996
  if (strcmp(type_str, "scalar") == 0) {
995
997
  VALUE text = rb_hash_aref(item, rb_str_new_cstr("text"));
@@ -51,11 +51,13 @@ module Yerba
51
51
  end
52
52
 
53
53
  def to_h
54
- value_at(ROOT_SELECTOR)
54
+ value = value_at(ROOT_SELECTOR)
55
+ value.nil? ? {} : value
55
56
  end
56
57
 
57
58
  def to_a
58
- value_at(ROOT_SELECTOR)
59
+ value = value_at(ROOT_SELECTOR)
60
+ value.nil? ? [] : value
59
61
  end
60
62
 
61
63
  def to_yaml
@@ -85,14 +87,20 @@ module Yerba
85
87
  end
86
88
 
87
89
  def find_by(...)
90
+ return nil if empty_root?
91
+
88
92
  root.find_by(...)
89
93
  end
90
94
 
91
95
  def where(...)
96
+ return QueryResult.new(nil, []) if empty_root?
97
+
92
98
  root.where(...)
93
99
  end
94
100
 
95
101
  def pluck(...)
102
+ return [] if empty_root?
103
+
96
104
  root.pluck(...)
97
105
  end
98
106
 
@@ -169,6 +177,10 @@ module Yerba
169
177
 
170
178
  private
171
179
 
180
+ def empty_root?
181
+ root.is_a?(Scalar) && root.value.nil?
182
+ end
183
+
172
184
  def check_stale!
173
185
  return unless stale?
174
186
 
data/lib/yerba/map.rb CHANGED
@@ -123,6 +123,28 @@ module Yerba
123
123
  end
124
124
  end
125
125
 
126
+ # Deletes a key from the map, or deletes the entire map node from the document.
127
+ #
128
+ # When +key+ is given and the map is connected to a document, deletes that key
129
+ # from the map in the YAML document. When no +key+ is given and the map is
130
+ # connected, removes the entire map node at its selector path. When not connected
131
+ # to a document, delegates to the underlying Hash.
132
+ #
133
+ # Returns +self+.
134
+ #
135
+ # # Delete a specific key from a connected map:
136
+ # document = Yerba::Document.parse(<<~YAML)
137
+ # database:
138
+ # host: localhost
139
+ # pool: 10
140
+ # YAML
141
+ #
142
+ # document["database"].delete("pool")
143
+ # document.to_s # => "database:\n host: localhost\n"
144
+ #
145
+ # # Delete the entire map node:
146
+ # document["database"].delete
147
+ # document.to_s # => "{}\n"
126
148
  def delete(key = nil)
127
149
  if key && connected?
128
150
  new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
data/lib/yerba/node.rb CHANGED
@@ -36,14 +36,20 @@ module Yerba
36
36
  instance
37
37
  end
38
38
 
39
- def from(file_path:, selector:, line: nil, **attributes)
39
+ def from(file_path:, selector:, line: nil, location: nil, **attributes)
40
40
  instance = allocate
41
41
 
42
- instance.send(:init_node, nil, selector, nil, nil, file_path, line)
42
+ instance.send(:init_node, nil, selector, coerce_location(location), nil, file_path, line)
43
43
  instance.send(:init_from, **attributes) if instance.respond_to?(:init_from, true)
44
44
 
45
45
  instance
46
46
  end
47
+
48
+ def coerce_location(location)
49
+ return location unless location.is_a?(Hash)
50
+
51
+ Location.new(**location.transform_keys(&:to_sym))
52
+ end
47
53
  end
48
54
 
49
55
  def self.included(base)
@@ -258,6 +258,21 @@ module Yerba
258
258
  self
259
259
  end
260
260
 
261
+ # Deletes the entire sequence node from the YAML document.
262
+ #
263
+ # Removes the sequence at its selector path from the parent document.
264
+ # Has no effect if the sequence is not connected to a document.
265
+ #
266
+ # Returns the document, or +nil+ if the sequence is not connected to a document.
267
+ #
268
+ # document = Yerba::Document.parse(<<~YAML)
269
+ # items:
270
+ # - name: Ruby
271
+ # - name: Rust
272
+ # YAML
273
+ #
274
+ # document["items"].delete
275
+ # document.to_s # => "{}\n"
261
276
  def delete
262
277
  document&.delete(@selector)
263
278
  end
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.3"
4
+ VERSION = "0.7.5"
5
5
  end
data/rust/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "yerba"
3
- version = "0.7.3"
3
+ version = "0.7.5"
4
4
  edition = "2021"
5
5
  authors = ["Marco Roth <marco.roth@intergga.ch>"]
6
6
  description = "YAML Editing and Refactoring with Better Accuracy"
@@ -11,8 +11,8 @@ impl Document {
11
11
 
12
12
  if source_parent == destination_parent {
13
13
  let (parent_path, source_key) = source_path.rsplit_once('.').unwrap_or(("", source_path));
14
- let parent_node = self.navigate(parent_path)?;
15
14
 
15
+ let parent_node = self.navigate(parent_path)?;
16
16
  let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
17
17
 
18
18
  let entry = find_entry_by_key(&map, source_key).ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
@@ -39,9 +39,10 @@ impl Document {
39
39
 
40
40
  match last_segment {
41
41
  crate::selector::SelectorSegment::Key(last_key) => {
42
- let has_wildcard = selector.has_wildcard() || selector.has_brackets();
42
+ let has_wildcard = selector.has_wildcard();
43
+ let has_brackets = selector.has_brackets();
43
44
 
44
- if parent_path.is_empty() && !has_wildcard {
45
+ if parent_path.is_empty() && (!has_wildcard || !has_brackets) {
45
46
  let parent_node = self.navigate(&parent_path)?;
46
47
  let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
47
48
 
@@ -60,11 +61,14 @@ impl Document {
60
61
  return Err(YerbaError::SelectorNotFound(dot_path.to_string()));
61
62
  }
62
63
 
63
- if parent_nodes.len() == 1 && !has_wildcard {
64
+ if parent_nodes.len() == 1 && (!has_wildcard || !has_brackets) {
64
65
  let map = find_block_map(&parent_nodes[0]).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
65
-
66
66
  let entry = find_entry_by_key(&map, last_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
67
67
 
68
+ if map.entries().count() == 1 {
69
+ return self.collapse_to_empty_flow_map(&parent_nodes[0]);
70
+ }
71
+
68
72
  return self.remove_map_entry(&entry);
69
73
  }
70
74
 
@@ -104,6 +108,10 @@ impl Document {
104
108
  })
105
109
  .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} item '{}'", dot_path, value)))?;
106
110
 
111
+ if sequence.entries().count() == 1 {
112
+ return self.collapse_to_empty_flow_sequence(&current_node);
113
+ }
114
+
107
115
  self.remove_node(target_entry.syntax())
108
116
  }
109
117
 
@@ -120,6 +128,67 @@ impl Document {
120
128
  return Err(YerbaError::IndexOutOfBounds(index, entries.len()));
121
129
  }
122
130
 
131
+ if entries.len() == 1 {
132
+ return self.collapse_to_empty_flow_sequence(&current_node);
133
+ }
134
+
123
135
  self.remove_node(entries[index].syntax())
124
136
  }
137
+
138
+ fn collapse_to_empty_flow_map(&mut self, current_node: &SyntaxNode) -> Result<(), YerbaError> {
139
+ let map_value = current_node
140
+ .descendants()
141
+ .find(|descendant| descendant.kind() == SyntaxKind::BLOCK_MAP_VALUE)
142
+ .ok_or_else(|| YerbaError::SelectorNotFound("Expected a map value".to_string()))?;
143
+
144
+ let mut range = map_value.text_range();
145
+
146
+ if let Some(previous) = map_value.prev_sibling_or_token().and_then(|element| element.into_token()) {
147
+ if previous.kind() == SyntaxKind::WHITESPACE && previous.text().contains('\n') {
148
+ range = TextRange::new(previous.text_range().start(), range.end());
149
+ }
150
+ }
151
+
152
+ self.apply_edit(range, " {}")
153
+ }
154
+
155
+ fn collapse_to_empty_flow_sequence(&mut self, current_node: &SyntaxNode) -> Result<(), YerbaError> {
156
+ if current_node.kind() == SyntaxKind::BLOCK_MAP_VALUE {
157
+ let mut range = current_node.text_range();
158
+ let mut comment_text: Option<String> = None;
159
+ let mut sibling = current_node.prev_sibling_or_token();
160
+
161
+ while let Some(ref element) = sibling {
162
+ match element {
163
+ rowan::NodeOrToken::Token(token) => {
164
+ if token.kind() == SyntaxKind::COMMENT {
165
+ comment_text = Some(token.text().to_string());
166
+ range = TextRange::new(token.text_range().start(), range.end());
167
+ sibling = token.prev_sibling_or_token();
168
+ } else if token.kind() == SyntaxKind::WHITESPACE {
169
+ range = TextRange::new(token.text_range().start(), range.end());
170
+ sibling = token.prev_sibling_or_token();
171
+ } else {
172
+ break;
173
+ }
174
+ }
175
+ _ => break,
176
+ }
177
+ }
178
+
179
+ let replacement = match comment_text {
180
+ Some(comment) => format!(" [] {}", comment),
181
+ None => " []".to_string(),
182
+ };
183
+
184
+ return self.apply_edit(range, &replacement);
185
+ }
186
+
187
+ let sequence = current_node
188
+ .descendants()
189
+ .find_map(BlockSeq::cast)
190
+ .ok_or_else(|| YerbaError::NotASequence("Expected a sequence".to_string()))?;
191
+
192
+ self.apply_edit(removal_range(sequence.syntax()), "[]")
193
+ }
125
194
  }
@@ -90,8 +90,10 @@ impl Document {
90
90
  .navigate_all_compact(dot_path)
91
91
  .iter()
92
92
  .map(|node| {
93
- let offset: usize = node.text_range().start().into();
93
+ let range = node.text_range();
94
+ let offset: usize = range.start().into();
94
95
  let line = line_at(&source, offset);
96
+ let location = compute_location(&source, offset, range.end().into());
95
97
  let selector = node_selector(node);
96
98
  let is_scalar = !node
97
99
  .descendants()
@@ -121,6 +123,7 @@ impl Document {
121
123
  file_path: file_path.clone(),
122
124
  selector,
123
125
  line,
126
+ location,
124
127
  }
125
128
  })
126
129
  .collect()
@@ -19,6 +19,7 @@ pub struct LocatedNode {
19
19
  pub file_path: Option<String>,
20
20
  pub selector: String,
21
21
  pub line: usize,
22
+ pub location: Location,
22
23
  }
23
24
 
24
25
  use std::fs;
data/rust/src/ffi.rs CHANGED
@@ -843,6 +843,14 @@ pub unsafe extern "C" fn yerba_glob_get(glob_pattern: *const c_char, path: *cons
843
843
  "node_type": node.node_type,
844
844
  "selector": node.selector,
845
845
  "line": node.line,
846
+ "location": {
847
+ "start_line": node.location.start_line,
848
+ "start_column": node.location.start_column,
849
+ "end_line": node.location.end_line,
850
+ "end_column": node.location.end_column,
851
+ "start_offset": node.location.start_offset,
852
+ "end_offset": node.location.end_offset,
853
+ },
846
854
  });
847
855
 
848
856
  if let Some(file_path) = &node.file_path {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yerba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Roth