yerba 0.7.3 → 0.7.4

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: bfa98c0309844cc745efbdea0a559825a797ca9eba6eaf2d63e39b2e5098333c
4
+ data.tar.gz: 270f11efd66edf472dba1a5a524db66bcdc24f1eba081c4e5b267853f275111b
5
5
  SHA512:
6
- metadata.gz: bf1fdc98836e461bd02830a07008ba86df5fd71d22d8ff478e042c5c36e89caad9cb006e5c20ac191a3c24c11fe33add62d12109e1269225464247093d549142
7
- data.tar.gz: 85fa2fac4eff2cf7662a2549b6b5dac953e4a3646e71b405774ebd39ec2200604429ea8cd11724385ae801248a7250e94c53a4cd41b9c4f126b6d93f6ec8fa71
6
+ metadata.gz: 2bf52ab64c03b3eaeafaefe547b4cbe3cf67c21c19025aa21da00afe4e36edbc2d8bf3ae33ce06d744fa8616da37aed189e7e91afab3cc904fec63151aeef667
7
+ data.tar.gz: edc83ce4ee21753401e258deb33e54c7a206c13ab8b909a9d54595295e396caf0a3eaa58a33746205d5a955d8b62d04da0e9473423adcf118bc1a1cd6a955d67
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"));
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.4"
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.4"
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.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Roth