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 +4 -4
- data/ext/yerba/yerba.c +2 -0
- data/lib/yerba/map.rb +22 -0
- data/lib/yerba/node.rb +8 -2
- data/lib/yerba/sequence.rb +15 -0
- data/lib/yerba/version.rb +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/document/delete.rs +74 -5
- data/rust/src/document/get.rs +4 -1
- data/rust/src/document/mod.rs +1 -0
- data/rust/src/ffi.rs +8 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bfa98c0309844cc745efbdea0a559825a797ca9eba6eaf2d63e39b2e5098333c
|
|
4
|
+
data.tar.gz: 270f11efd66edf472dba1a5a524db66bcdc24f1eba081c4e5b267853f275111b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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,
|
|
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)
|
data/lib/yerba/sequence.rb
CHANGED
|
@@ -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
data/rust/Cargo.toml
CHANGED
data/rust/src/document/delete.rs
CHANGED
|
@@ -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()
|
|
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(¤t_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(¤t_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
|
}
|
data/rust/src/document/get.rs
CHANGED
|
@@ -90,8 +90,10 @@ impl Document {
|
|
|
90
90
|
.navigate_all_compact(dot_path)
|
|
91
91
|
.iter()
|
|
92
92
|
.map(|node| {
|
|
93
|
-
let
|
|
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()
|
data/rust/src/document/mod.rs
CHANGED
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 {
|