yerba 0.7.0-arm-linux-gnu → 0.7.2-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 +4 -4
- data/exe/arm-linux-gnu/yerba +0 -0
- data/ext/yerba/include/yerba.h +5 -0
- data/ext/yerba/yerba.c +16 -0
- data/lib/yerba/3.2/yerba.so +0 -0
- data/lib/yerba/3.3/yerba.so +0 -0
- data/lib/yerba/3.4/yerba.so +0 -0
- data/lib/yerba/4.0/yerba.so +0 -0
- data/lib/yerba/document.rb +21 -1
- data/lib/yerba/map.rb +4 -0
- data/lib/yerba/node.rb +6 -0
- data/lib/yerba/sequence.rb +4 -0
- data/lib/yerba/version.rb +1 -1
- data/lib/yerba.rb +1 -0
- data/rust/Cargo.lock +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/document/mod.rs +5 -0
- data/rust/src/document/style.rs +102 -24
- data/rust/src/ffi.rs +12 -0
- data/rust/src/yerbafile.rs +23 -3
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b4a184098ccba1de65340ff7812ed2e99dd2c4e040f5091cb17a4a1301e55b3
|
|
4
|
+
data.tar.gz: 923fae627e8e773b8192e82d4860bfa2c2ea28db670c32a67a9125b61485c53f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 65dcad11c2c0dc109ab61757c64b15724190a9d587e8b1bddc0a5f76d2cae1a7da84a52f2b785bbd2a9cbf22d8e42f07174b4e5383c0e4a54d3e8275aa6efb47
|
|
7
|
+
data.tar.gz: 5aa046c0d8357e3acbc732faacb7950a3db6e233780ed3a653f89405c8eef64998a918b72711a125a485dad5448ad8251f38b9b643250459d428d1b9d6df3428
|
data/exe/arm-linux-gnu/yerba
CHANGED
|
Binary file
|
data/ext/yerba/include/yerba.h
CHANGED
|
@@ -72,6 +72,11 @@ void yerba_document_free(struct Document *document);
|
|
|
72
72
|
|
|
73
73
|
struct YerbaGetResult yerba_document_get(const struct Document *document, const char *path);
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Caller must free with yerba_string_free.
|
|
77
|
+
*/
|
|
78
|
+
char *yerba_document_source(const struct Document *document, const char *path);
|
|
79
|
+
|
|
75
80
|
/**
|
|
76
81
|
* Caller must free with yerba_string_free.
|
|
77
82
|
*/
|
data/ext/yerba/yerba.c
CHANGED
|
@@ -119,10 +119,12 @@ static VALUE document_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
119
119
|
if (NIL_P(path)) {
|
|
120
120
|
result = yerba_document_parse("---\n");
|
|
121
121
|
rb_iv_set(self, "@path", Qnil);
|
|
122
|
+
rb_iv_set(self, "@loaded_mtime", Qnil);
|
|
122
123
|
} else {
|
|
123
124
|
const char *file_path = StringValueCStr(path);
|
|
124
125
|
result = yerba_document_parse_file(file_path);
|
|
125
126
|
rb_iv_set(self, "@path", path);
|
|
127
|
+
rb_iv_set(self, "@loaded_mtime", rb_funcall(rb_cFile, rb_intern("mtime"), 1, path));
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
if (!result.document) {
|
|
@@ -297,6 +299,19 @@ static VALUE document_valid_selector_p(VALUE self, VALUE path) {
|
|
|
297
299
|
return yerba_document_valid_selector(document, StringValueCStr(path)) ? Qtrue : Qfalse;
|
|
298
300
|
}
|
|
299
301
|
|
|
302
|
+
/* document.source(path) → raw YAML text of the node at path */
|
|
303
|
+
static VALUE document_source(VALUE self, VALUE path) {
|
|
304
|
+
struct Document *document = get_document(self);
|
|
305
|
+
char *text = yerba_document_source(document, StringValueCStr(path));
|
|
306
|
+
|
|
307
|
+
if (!text) return Qnil;
|
|
308
|
+
|
|
309
|
+
VALUE result = make_utf8_string(text);
|
|
310
|
+
yerba_string_free(text);
|
|
311
|
+
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
|
|
300
315
|
/* document.get_value(path) → parsed Ruby object (Hash/Array/String/Integer/etc) */
|
|
301
316
|
static VALUE document_get_value(VALUE self, VALUE path) {
|
|
302
317
|
struct Document *document = get_document(self);
|
|
@@ -1091,6 +1106,7 @@ void Init_yerba(void) {
|
|
|
1091
1106
|
rb_define_method(rb_cDocument, "blank_lines", document_blank_lines, 2);
|
|
1092
1107
|
rb_define_method(rb_cDocument, "apply_yerbafile", document_apply_yerbafile, -1);
|
|
1093
1108
|
rb_define_method(rb_cDocument, "validate_schema", document_validate_schema, -1);
|
|
1109
|
+
rb_define_method(rb_cDocument, "source", document_source, 1);
|
|
1094
1110
|
rb_define_method(rb_cDocument, "to_s", document_to_s, 0);
|
|
1095
1111
|
rb_define_method(rb_cDocument, "write!", document_save, 0);
|
|
1096
1112
|
rb_define_method(rb_cDocument, "changed?", document_changed_p, 0);
|
data/lib/yerba/3.2/yerba.so
CHANGED
|
Binary file
|
data/lib/yerba/3.3/yerba.so
CHANGED
|
Binary file
|
data/lib/yerba/3.4/yerba.so
CHANGED
|
Binary file
|
data/lib/yerba/4.0/yerba.so
CHANGED
|
Binary file
|
data/lib/yerba/document.rb
CHANGED
|
@@ -64,9 +64,16 @@ module Yerba
|
|
|
64
64
|
|
|
65
65
|
def save_to!(path)
|
|
66
66
|
@path = path
|
|
67
|
+
@loaded_mtime = nil
|
|
67
68
|
save!
|
|
68
69
|
end
|
|
69
70
|
|
|
71
|
+
def stale?
|
|
72
|
+
return false unless @path && @loaded_mtime
|
|
73
|
+
|
|
74
|
+
File.mtime(@path) != @loaded_mtime
|
|
75
|
+
end
|
|
76
|
+
|
|
70
77
|
def dig(*keys)
|
|
71
78
|
keys.reduce(self) { |node, key| node.nil? ? nil : node[key] }
|
|
72
79
|
end
|
|
@@ -106,15 +113,18 @@ module Yerba
|
|
|
106
113
|
end
|
|
107
114
|
|
|
108
115
|
def save!(apply: false)
|
|
116
|
+
check_stale!
|
|
109
117
|
Yerbafile.apply!(self, apply) if apply
|
|
110
118
|
write!
|
|
111
|
-
|
|
119
|
+
@loaded_mtime = File.mtime(@path) if @path
|
|
112
120
|
self
|
|
113
121
|
end
|
|
114
122
|
|
|
115
123
|
def apply!(yerbafile = nil)
|
|
124
|
+
check_stale!
|
|
116
125
|
apply(yerbafile)
|
|
117
126
|
write! if changed?
|
|
127
|
+
@loaded_mtime = File.mtime(@path) if @path
|
|
118
128
|
|
|
119
129
|
self
|
|
120
130
|
end
|
|
@@ -157,6 +167,16 @@ module Yerba
|
|
|
157
167
|
raise Yerba::SelectorNotFoundError, message
|
|
158
168
|
end
|
|
159
169
|
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
def check_stale!
|
|
173
|
+
return unless stale?
|
|
174
|
+
|
|
175
|
+
raise Yerba::StaleFileError, "#{@path} was modified externally since it was loaded"
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
public
|
|
179
|
+
|
|
160
180
|
def inspect
|
|
161
181
|
if path
|
|
162
182
|
"#<Yerba::Document path=#{path.inspect}>"
|
data/lib/yerba/map.rb
CHANGED
data/lib/yerba/node.rb
CHANGED
|
@@ -20,6 +20,12 @@ module Yerba
|
|
|
20
20
|
!@document.nil? || !@file_path.nil?
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
def source
|
|
24
|
+
return nil unless connected? && @selector
|
|
25
|
+
|
|
26
|
+
document.source(@selector)
|
|
27
|
+
end
|
|
28
|
+
|
|
23
29
|
module ClassMethods
|
|
24
30
|
def from_document(document, selector, location = nil, key = nil, **attributes)
|
|
25
31
|
instance = allocate
|
data/lib/yerba/sequence.rb
CHANGED
data/lib/yerba/version.rb
CHANGED
data/lib/yerba.rb
CHANGED
|
@@ -30,6 +30,7 @@ module Yerba
|
|
|
30
30
|
class ExecutableNotFoundError < StandardError; end
|
|
31
31
|
class CompilationError < StandardError; end
|
|
32
32
|
class SelectorNotFoundError < StandardError; end
|
|
33
|
+
class StaleFileError < StandardError; end
|
|
33
34
|
|
|
34
35
|
GEM_NAME = "yerba"
|
|
35
36
|
EXECUTABLE_NAME = "yerba"
|
data/rust/Cargo.lock
CHANGED
data/rust/Cargo.toml
CHANGED
data/rust/src/document/mod.rs
CHANGED
|
@@ -159,6 +159,11 @@ impl Document {
|
|
|
159
159
|
Ok(())
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
pub fn source(&self, dot_path: &str) -> Result<String, YerbaError> {
|
|
163
|
+
let node = self.navigate(dot_path)?;
|
|
164
|
+
Ok(node.text().to_string())
|
|
165
|
+
}
|
|
166
|
+
|
|
162
167
|
pub fn navigate(&self, dot_path: &str) -> Result<SyntaxNode, YerbaError> {
|
|
163
168
|
Self::validate_path(dot_path)?;
|
|
164
169
|
|
data/rust/src/document/style.rs
CHANGED
|
@@ -406,42 +406,120 @@ impl Document {
|
|
|
406
406
|
}
|
|
407
407
|
|
|
408
408
|
loop {
|
|
409
|
-
let
|
|
410
|
-
|
|
411
|
-
|
|
409
|
+
let source = self.root.text().to_string();
|
|
410
|
+
|
|
411
|
+
let scope_node = if scope_path.is_empty() {
|
|
412
|
+
self.root.clone()
|
|
413
|
+
} else {
|
|
414
|
+
match self.navigate(scope_path) {
|
|
415
|
+
Ok(node) => node,
|
|
416
|
+
Err(_) => return Ok(()),
|
|
417
|
+
}
|
|
412
418
|
};
|
|
413
419
|
|
|
414
|
-
let mut
|
|
420
|
+
let mut mismatch_ranges: Vec<TextRange> = Vec::new();
|
|
421
|
+
|
|
422
|
+
for descendant in scope_node.descendants() {
|
|
423
|
+
if !BlockSeq::can_cast(descendant.kind()) {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
let parent_entry = match descendant.ancestors().find(|ancestor| ancestor.kind() == SyntaxKind::BLOCK_MAP_ENTRY) {
|
|
428
|
+
Some(entry) => entry,
|
|
429
|
+
None => continue,
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
let first_entry = match BlockSeq::cast(descendant.clone()).and_then(|sequence| sequence.entries().next()) {
|
|
433
|
+
Some(entry) => entry,
|
|
434
|
+
None => continue,
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
let key_indent = preceding_whitespace_indent(&parent_entry).len();
|
|
438
|
+
let entry_indent = preceding_whitespace_indent(first_entry.syntax()).len();
|
|
439
|
+
let is_indented = entry_indent > key_indent;
|
|
415
440
|
|
|
416
|
-
|
|
417
|
-
|
|
441
|
+
if (style == "indented" && !is_indented) || (style == "compact" && is_indented) {
|
|
442
|
+
mismatch_ranges.push(descendant.text_range());
|
|
443
|
+
}
|
|
418
444
|
}
|
|
419
445
|
|
|
420
|
-
|
|
446
|
+
if mismatch_ranges.is_empty() {
|
|
447
|
+
return Ok(());
|
|
448
|
+
}
|
|
421
449
|
|
|
422
|
-
let
|
|
423
|
-
.into_iter()
|
|
424
|
-
.flat_map(|selector| {
|
|
425
|
-
if selector.contains("[]") {
|
|
426
|
-
self.resolve_selectors(&selector)
|
|
427
|
-
} else {
|
|
428
|
-
vec![selector]
|
|
429
|
-
}
|
|
430
|
-
})
|
|
431
|
-
.collect();
|
|
450
|
+
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
432
451
|
|
|
433
|
-
|
|
434
|
-
.
|
|
435
|
-
|
|
436
|
-
|
|
452
|
+
for range in &mismatch_ranges {
|
|
453
|
+
if mismatch_ranges.iter().any(|other| other != range && range.contains_range(*other)) {
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
437
456
|
|
|
438
|
-
|
|
457
|
+
let node = match scope_node
|
|
458
|
+
.descendants()
|
|
459
|
+
.find(|descendant| BlockSeq::can_cast(descendant.kind()) && descendant.text_range() == *range)
|
|
460
|
+
{
|
|
461
|
+
Some(node) => node,
|
|
462
|
+
None => continue,
|
|
463
|
+
};
|
|
464
|
+
let parent_entry = match node.ancestors().find(|ancestor| ancestor.kind() == SyntaxKind::BLOCK_MAP_ENTRY) {
|
|
465
|
+
Some(entry) => entry,
|
|
466
|
+
None => continue,
|
|
467
|
+
};
|
|
468
|
+
let first_entry = match BlockSeq::cast(node).and_then(|seq| seq.entries().next()) {
|
|
469
|
+
Some(entry) => entry,
|
|
470
|
+
None => continue,
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
let key_indent = preceding_whitespace_indent(&parent_entry).len();
|
|
474
|
+
let entry_indent = preceding_whitespace_indent(first_entry.syntax()).len();
|
|
475
|
+
|
|
476
|
+
let indent_diff: i32 = match style {
|
|
477
|
+
"indented" => (key_indent as i32 + 2) - entry_indent as i32,
|
|
478
|
+
"compact" => key_indent as i32 - entry_indent as i32,
|
|
479
|
+
_ => continue,
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
if indent_diff == 0 {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
let seq_start: usize = range.start().into();
|
|
487
|
+
let line_start = source[..seq_start].rfind('\n').map(|pos| pos + 1).unwrap_or(0);
|
|
488
|
+
let full_text = &source[line_start..usize::from(range.end())];
|
|
489
|
+
|
|
490
|
+
let reindented: String = full_text
|
|
491
|
+
.lines()
|
|
492
|
+
.map(|line| {
|
|
493
|
+
if line.trim().is_empty() {
|
|
494
|
+
String::new()
|
|
495
|
+
} else {
|
|
496
|
+
let current_indent = line.len() - line.trim_start().len();
|
|
497
|
+
let new_indent = (current_indent as i32 + indent_diff).max(0) as usize;
|
|
498
|
+
format!("{}{}", " ".repeat(new_indent), line.trim_start())
|
|
499
|
+
}
|
|
500
|
+
})
|
|
501
|
+
.collect::<Vec<_>>()
|
|
502
|
+
.join("\n");
|
|
503
|
+
|
|
504
|
+
edits.push((TextRange::new(rowan::TextSize::from(line_start as u32), range.end()), reindented));
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if edits.is_empty() {
|
|
439
508
|
return Ok(());
|
|
440
509
|
}
|
|
441
510
|
|
|
442
|
-
|
|
511
|
+
edits.sort_by_key(|edit| std::cmp::Reverse(edit.0.start()));
|
|
512
|
+
|
|
513
|
+
let mut new_source = source;
|
|
514
|
+
for (range, replacement) in edits {
|
|
515
|
+
let start: usize = range.start().into();
|
|
516
|
+
let end: usize = range.end().into();
|
|
517
|
+
new_source.replace_range(start..end, &replacement);
|
|
518
|
+
}
|
|
443
519
|
|
|
444
|
-
self.
|
|
520
|
+
let path = self.path.take();
|
|
521
|
+
*self = Self::parse(&new_source)?;
|
|
522
|
+
self.path = path;
|
|
445
523
|
}
|
|
446
524
|
}
|
|
447
525
|
|
data/rust/src/ffi.rs
CHANGED
|
@@ -278,6 +278,18 @@ fn location_to_ffi(location: crate::Location) -> YerbaLocation {
|
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
/// Caller must free with yerba_string_free.
|
|
282
|
+
#[no_mangle]
|
|
283
|
+
pub unsafe extern "C" fn yerba_document_source(document: *const Document, path: *const c_char) -> *mut c_char {
|
|
284
|
+
let document = &*document;
|
|
285
|
+
let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
|
|
286
|
+
|
|
287
|
+
match document.source(selector_string) {
|
|
288
|
+
Ok(text) => CString::new(text).unwrap_or_default().into_raw(),
|
|
289
|
+
Err(_) => ptr::null_mut(),
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
281
293
|
/// Caller must free with yerba_string_free.
|
|
282
294
|
#[no_mangle]
|
|
283
295
|
pub unsafe extern "C" fn yerba_document_get_value(document: *const Document, path: *const c_char) -> *mut c_char {
|
data/rust/src/yerbafile.rs
CHANGED
|
@@ -439,6 +439,24 @@ impl Yerbafile {
|
|
|
439
439
|
results
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
+
fn relativize_path(&self, file_path: &str) -> Option<String> {
|
|
443
|
+
let path = Path::new(file_path);
|
|
444
|
+
|
|
445
|
+
if !path.is_absolute() {
|
|
446
|
+
return None;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if let Some(directory) = &self.directory {
|
|
450
|
+
if let Ok(relative) = path.strip_prefix(directory) {
|
|
451
|
+
return Some(relative.to_string_lossy().to_string());
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
std::env::current_dir()
|
|
456
|
+
.ok()
|
|
457
|
+
.and_then(|cwd| path.strip_prefix(&cwd).ok().map(|relative| relative.to_string_lossy().to_string()))
|
|
458
|
+
}
|
|
459
|
+
|
|
442
460
|
fn should_run_global_pipeline(&self, file_path: &str) -> bool {
|
|
443
461
|
if self.pipeline.is_empty() {
|
|
444
462
|
return false;
|
|
@@ -454,15 +472,17 @@ impl Yerbafile {
|
|
|
454
472
|
|
|
455
473
|
pub fn apply_to_document(&self, document: &mut Document, file_path: &str) -> Result<bool, YerbaError> {
|
|
456
474
|
let original = document.to_string();
|
|
475
|
+
let relative_path = self.relativize_path(file_path);
|
|
476
|
+
let match_path = relative_path.as_deref().unwrap_or(file_path);
|
|
457
477
|
|
|
458
|
-
if self.should_run_global_pipeline(
|
|
478
|
+
if self.should_run_global_pipeline(match_path) {
|
|
459
479
|
execute_pipeline(document, &self.pipeline, None, file_path, self)?;
|
|
460
480
|
}
|
|
461
481
|
|
|
462
482
|
for rule in &self.rules {
|
|
463
|
-
if !
|
|
483
|
+
if !match_path.is_empty() {
|
|
464
484
|
if let Ok(pattern) = glob::Pattern::new(&rule.files) {
|
|
465
|
-
if !pattern.matches(
|
|
485
|
+
if !pattern.matches(match_path) && !pattern.matches_path(Path::new(match_path)) {
|
|
466
486
|
continue;
|
|
467
487
|
}
|
|
468
488
|
} else {
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yerba
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.2
|
|
5
5
|
platform: arm-linux-gnu
|
|
6
6
|
authors:
|
|
7
7
|
- Marco Roth
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A CLI tool for editing YAML while preserving structure, comments, and
|
|
14
14
|
format.
|