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.
- checksums.yaml +4 -4
- data/README.md +53 -17
- data/exe/arm-linux-gnu/yerba +0 -0
- data/ext/yerba/include/yerba.h +58 -35
- data/ext/yerba/yerba.c +281 -187
- 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/collection.rb +4 -4
- data/lib/yerba/document.rb +134 -0
- data/lib/yerba/formatting.rb +1 -0
- data/lib/yerba/location.rb +8 -1
- data/lib/yerba/map.rb +16 -6
- data/lib/yerba/node.rb +41 -0
- data/lib/yerba/query_result.rb +33 -10
- data/lib/yerba/scalar.rb +29 -0
- data/lib/yerba/selector.rb +3 -1
- data/lib/yerba/sequence.rb +38 -33
- data/lib/yerba/version.rb +1 -1
- data/rust/Cargo.lock +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/commands/get.rs +12 -12
- data/rust/src/commands/init.rs +8 -8
- data/rust/src/commands/insert.rs +4 -18
- data/rust/src/commands/location.rs +2 -0
- data/rust/src/commands/mod.rs +34 -6
- data/rust/src/commands/move_item.rs +1 -1
- data/rust/src/commands/move_key.rs +8 -8
- data/rust/src/commands/rename.rs +1 -1
- data/rust/src/commands/selectors.rs +1 -1
- data/rust/src/commands/set.rs +14 -6
- data/rust/src/commands/sort.rs +5 -1
- data/rust/src/document/condition.rs +25 -25
- data/rust/src/document/delete.rs +57 -43
- data/rust/src/document/get.rs +91 -82
- data/rust/src/document/insert.rs +77 -77
- data/rust/src/document/mod.rs +202 -76
- data/rust/src/document/nodes.rs +162 -0
- data/rust/src/document/schema.rs +8 -8
- data/rust/src/document/set.rs +49 -26
- data/rust/src/document/sort.rs +105 -60
- data/rust/src/document/style.rs +50 -49
- data/rust/src/document/unique.rs +5 -5
- data/rust/src/error.rs +8 -4
- data/rust/src/ffi.rs +234 -107
- data/rust/src/json.rs +62 -2
- data/rust/src/lib.rs +4 -2
- data/rust/src/main.rs +2 -1
- data/rust/src/selector.rs +48 -7
- data/rust/src/syntax.rs +449 -56
- data/rust/src/validation.rs +5 -3
- data/rust/src/yerbafile.rs +168 -94
- metadata +3 -2
data/rust/src/document/mod.rs
CHANGED
|
@@ -2,6 +2,7 @@ mod condition;
|
|
|
2
2
|
mod delete;
|
|
3
3
|
mod get;
|
|
4
4
|
mod insert;
|
|
5
|
+
mod nodes;
|
|
5
6
|
mod set;
|
|
6
7
|
mod sort;
|
|
7
8
|
mod unique;
|
|
@@ -32,6 +33,7 @@ pub struct LocatedNode {
|
|
|
32
33
|
use std::fs;
|
|
33
34
|
use std::path::{Path, PathBuf};
|
|
34
35
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
36
|
+
use std::sync::OnceLock;
|
|
35
37
|
|
|
36
38
|
use rowan::ast::AstNode;
|
|
37
39
|
use rowan::{TextRange, TextSize};
|
|
@@ -46,34 +48,34 @@ use crate::syntax::{
|
|
|
46
48
|
column_at, dedent_block_scalar, extract_scalar, extract_scalar_text, find_block_map, find_block_sequence, find_entry_by_key, find_flow_entry_by_key,
|
|
47
49
|
find_flow_map, find_flow_sequence, find_scalar_token, first_collection, flow_map_entries, flow_sequence_entries, format_scalar_value, in_flow_collection,
|
|
48
50
|
is_map_key, is_yaml_non_string, line_at, line_start_at, preceding_whitespace_indent, preceding_whitespace_token, raw_scalar_value, removal_range,
|
|
49
|
-
FirstCollection, ScalarValue,
|
|
51
|
+
FirstCollection, LineIndex, ScalarValue,
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
#[derive(Debug, Clone)]
|
|
53
55
|
pub struct SortField {
|
|
54
|
-
pub
|
|
56
|
+
pub selector: String,
|
|
55
57
|
pub ascending: bool,
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
impl SortField {
|
|
59
|
-
pub fn asc(
|
|
61
|
+
pub fn asc(selector: &str) -> Self {
|
|
60
62
|
SortField {
|
|
61
|
-
|
|
63
|
+
selector: selector.to_string(),
|
|
62
64
|
ascending: true,
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
pub fn desc(
|
|
68
|
+
pub fn desc(selector: &str) -> Self {
|
|
67
69
|
SortField {
|
|
68
|
-
|
|
70
|
+
selector: selector.to_string(),
|
|
69
71
|
ascending: false,
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
pub fn parse(input: &str) -> Self {
|
|
74
|
-
if let Some((
|
|
76
|
+
if let Some((selector, direction)) = input.rsplit_once(':') {
|
|
75
77
|
match direction {
|
|
76
|
-
"desc" | "descending" => SortField::desc(
|
|
78
|
+
"desc" | "descending" => SortField::desc(selector),
|
|
77
79
|
_ => SortField::asc(input),
|
|
78
80
|
}
|
|
79
81
|
} else {
|
|
@@ -82,7 +84,58 @@ impl SortField {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
pub fn parse_list(input: &str) -> Vec<Self> {
|
|
85
|
-
|
|
87
|
+
let mut fields = Vec::new();
|
|
88
|
+
let mut current = String::new();
|
|
89
|
+
let mut depth = 0usize;
|
|
90
|
+
let mut quote: Option<char> = None;
|
|
91
|
+
|
|
92
|
+
for character in input.chars() {
|
|
93
|
+
match (quote, character) {
|
|
94
|
+
(Some(open), _) if character == open => quote = None,
|
|
95
|
+
(Some(_), _) => {}
|
|
96
|
+
(None, '"' | '\'') => quote = Some(character),
|
|
97
|
+
(None, '[') => depth += 1,
|
|
98
|
+
(None, ']') => depth = depth.saturating_sub(1),
|
|
99
|
+
(None, ',') if depth == 0 => {
|
|
100
|
+
fields.push(std::mem::take(&mut current));
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
(None, _) if character.is_whitespace() && depth == 0 => {
|
|
104
|
+
fields.push(std::mem::take(&mut current));
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
_ => {}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
current.push(character);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
fields.push(current);
|
|
114
|
+
|
|
115
|
+
fields.iter().filter(|field| !field.is_empty()).map(|field| SortField::parse(field)).collect()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Deserialize)]
|
|
120
|
+
#[serde(rename_all = "lowercase")]
|
|
121
|
+
pub enum SortMissing {
|
|
122
|
+
First,
|
|
123
|
+
Last,
|
|
124
|
+
Keep,
|
|
125
|
+
Skip,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
impl std::str::FromStr for SortMissing {
|
|
129
|
+
type Err = String;
|
|
130
|
+
|
|
131
|
+
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
|
132
|
+
match input {
|
|
133
|
+
"first" => Ok(SortMissing::First),
|
|
134
|
+
"last" => Ok(SortMissing::Last),
|
|
135
|
+
"keep" => Ok(SortMissing::Keep),
|
|
136
|
+
"skip" => Ok(SortMissing::Skip),
|
|
137
|
+
_ => Err(format!("unknown missing: {} (expected first, last, keep or skip)", input)),
|
|
138
|
+
}
|
|
86
139
|
}
|
|
87
140
|
}
|
|
88
141
|
|
|
@@ -103,6 +156,8 @@ pub struct Location {
|
|
|
103
156
|
pub end_column: usize,
|
|
104
157
|
pub start_offset: usize,
|
|
105
158
|
pub end_offset: usize,
|
|
159
|
+
pub start_character_column: usize,
|
|
160
|
+
pub end_character_column: usize,
|
|
106
161
|
}
|
|
107
162
|
|
|
108
163
|
#[derive(Debug)]
|
|
@@ -127,6 +182,28 @@ pub enum InsertPosition {
|
|
|
127
182
|
FromSortOrder(Vec<String>),
|
|
128
183
|
}
|
|
129
184
|
|
|
185
|
+
impl InsertPosition {
|
|
186
|
+
pub fn before(target: impl Into<String>) -> Self {
|
|
187
|
+
let target = target.into();
|
|
188
|
+
|
|
189
|
+
if target.starts_with('.') {
|
|
190
|
+
InsertPosition::BeforeCondition(target)
|
|
191
|
+
} else {
|
|
192
|
+
InsertPosition::Before(target)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
pub fn after(target: impl Into<String>) -> Self {
|
|
197
|
+
let target = target.into();
|
|
198
|
+
|
|
199
|
+
if target.starts_with('.') {
|
|
200
|
+
InsertPosition::AfterCondition(target)
|
|
201
|
+
} else {
|
|
202
|
+
InsertPosition::After(target)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
130
207
|
static NEXT_GENERATION: AtomicU64 = AtomicU64::new(1);
|
|
131
208
|
|
|
132
209
|
#[derive(Debug)]
|
|
@@ -134,6 +211,7 @@ pub struct Document {
|
|
|
134
211
|
root: SyntaxNode,
|
|
135
212
|
path: Option<PathBuf>,
|
|
136
213
|
generation: u64,
|
|
214
|
+
line_index: OnceLock<LineIndex>,
|
|
137
215
|
}
|
|
138
216
|
|
|
139
217
|
impl Document {
|
|
@@ -146,6 +224,7 @@ impl Document {
|
|
|
146
224
|
root: tree,
|
|
147
225
|
path: None,
|
|
148
226
|
generation: NEXT_GENERATION.fetch_add(1, Ordering::Relaxed),
|
|
227
|
+
line_index: OnceLock::new(),
|
|
149
228
|
})
|
|
150
229
|
}
|
|
151
230
|
|
|
@@ -181,49 +260,49 @@ impl Document {
|
|
|
181
260
|
Ok(())
|
|
182
261
|
}
|
|
183
262
|
|
|
184
|
-
pub fn source(&self,
|
|
185
|
-
let node = self.navigate(
|
|
263
|
+
pub fn source(&self, selector: &str) -> Result<String, YerbaError> {
|
|
264
|
+
let node = self.navigate(selector)?;
|
|
186
265
|
Ok(node.text().to_string())
|
|
187
266
|
}
|
|
188
267
|
|
|
189
|
-
pub fn navigate(&self,
|
|
190
|
-
Self::
|
|
268
|
+
pub fn navigate(&self, selector: &str) -> Result<SyntaxNode, YerbaError> {
|
|
269
|
+
Self::validate_selector(selector)?;
|
|
191
270
|
|
|
192
|
-
if
|
|
193
|
-
let root = Root::cast(self.root.clone()).ok_or_else(|| YerbaError::SelectorNotFound(
|
|
271
|
+
if selector.is_empty() {
|
|
272
|
+
let root = Root::cast(self.root.clone()).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
|
|
194
273
|
|
|
195
|
-
let document = root.documents().next().ok_or_else(|| YerbaError::SelectorNotFound(
|
|
274
|
+
let document = root.documents().next().ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
|
|
196
275
|
|
|
197
276
|
return Ok(document.syntax().clone());
|
|
198
277
|
}
|
|
199
278
|
|
|
200
|
-
let nodes = self.navigate_all_compact(
|
|
279
|
+
let nodes = self.navigate_all_compact(selector);
|
|
201
280
|
|
|
202
281
|
match nodes.len() {
|
|
203
|
-
0 => Err(YerbaError::SelectorNotFound(
|
|
282
|
+
0 => Err(YerbaError::SelectorNotFound(selector.to_string())),
|
|
204
283
|
1 => Ok(nodes.into_iter().next().unwrap()),
|
|
205
|
-
_ => Err(YerbaError::AmbiguousSelector(
|
|
284
|
+
_ => Err(YerbaError::AmbiguousSelector(selector.to_string(), nodes.len())),
|
|
206
285
|
}
|
|
207
286
|
}
|
|
208
287
|
|
|
209
|
-
pub fn navigate_all_compact(&self,
|
|
210
|
-
self.navigate_all(
|
|
288
|
+
pub fn navigate_all_compact(&self, selector: &str) -> Vec<SyntaxNode> {
|
|
289
|
+
self.navigate_all(selector).into_iter().flatten().collect()
|
|
211
290
|
}
|
|
212
291
|
|
|
213
|
-
pub(crate) fn refuse_flow_target(&self,
|
|
214
|
-
if self.navigate_all_compact(
|
|
215
|
-
return Err(YerbaError::FlowCollectionNotWritable(
|
|
292
|
+
pub(crate) fn refuse_flow_target(&self, selector: &str) -> Result<(), YerbaError> {
|
|
293
|
+
if self.navigate_all_compact(selector).iter().any(in_flow_collection) {
|
|
294
|
+
return Err(YerbaError::FlowCollectionNotWritable(selector.to_string()));
|
|
216
295
|
}
|
|
217
296
|
|
|
218
297
|
Ok(())
|
|
219
298
|
}
|
|
220
299
|
|
|
221
|
-
pub fn navigate_all(&self,
|
|
222
|
-
if Document::
|
|
300
|
+
pub fn navigate_all(&self, selector: &str) -> Vec<Option<SyntaxNode>> {
|
|
301
|
+
if Document::validate_selector(selector).is_err() {
|
|
223
302
|
return Vec::new();
|
|
224
303
|
}
|
|
225
304
|
|
|
226
|
-
let parsed = crate::selector::Selector::parse(
|
|
305
|
+
let parsed = crate::selector::Selector::parse(selector);
|
|
227
306
|
|
|
228
307
|
let root = match Root::cast(self.root.clone()) {
|
|
229
308
|
Some(root) => root,
|
|
@@ -299,21 +378,21 @@ impl Document {
|
|
|
299
378
|
current_nodes
|
|
300
379
|
}
|
|
301
380
|
|
|
302
|
-
pub fn
|
|
303
|
-
if
|
|
304
|
-
return Err(YerbaError::ParseError(format!("invalid
|
|
381
|
+
pub fn validate_selector(selector: &str) -> Result<(), YerbaError> {
|
|
382
|
+
if selector.ends_with('.') {
|
|
383
|
+
return Err(YerbaError::ParseError(format!("invalid selector: trailing dot in '{}'", selector)));
|
|
305
384
|
}
|
|
306
385
|
|
|
307
|
-
if
|
|
308
|
-
return Err(YerbaError::ParseError(format!("invalid
|
|
386
|
+
if selector.contains("..") {
|
|
387
|
+
return Err(YerbaError::ParseError(format!("invalid selector: double dot in '{}'", selector)));
|
|
309
388
|
}
|
|
310
389
|
|
|
311
|
-
if
|
|
312
|
-
return Err(YerbaError::ParseError(format!("invalid
|
|
390
|
+
if selector.starts_with('.') {
|
|
391
|
+
return Err(YerbaError::ParseError(format!("invalid selector: leading dot in '{}'", selector)));
|
|
313
392
|
}
|
|
314
393
|
|
|
315
|
-
if
|
|
316
|
-
return Err(YerbaError::ParseError(format!("invalid
|
|
394
|
+
if selector.contains('[') && !selector.contains(']') {
|
|
395
|
+
return Err(YerbaError::ParseError(format!("invalid selector: unclosed bracket in '{}'", selector)));
|
|
317
396
|
}
|
|
318
397
|
|
|
319
398
|
Ok(())
|
|
@@ -487,6 +566,10 @@ impl Document {
|
|
|
487
566
|
self.root.text().to_string()
|
|
488
567
|
}
|
|
489
568
|
|
|
569
|
+
fn line_index(&self) -> &LineIndex {
|
|
570
|
+
self.line_index.get_or_init(|| LineIndex::new(&self.source_text()))
|
|
571
|
+
}
|
|
572
|
+
|
|
490
573
|
fn reparse(&mut self, new_source: &str) -> Result<(), YerbaError> {
|
|
491
574
|
let document = Self::parse(new_source)?;
|
|
492
575
|
let path = self.path.take();
|
|
@@ -538,17 +621,19 @@ fn check_duplicate_keys(root: &SyntaxNode) -> Result<(), YerbaError> {
|
|
|
538
621
|
Ok(())
|
|
539
622
|
}
|
|
540
623
|
|
|
541
|
-
pub(crate) fn compute_location(
|
|
542
|
-
let start = start_offset.min(
|
|
543
|
-
let end = end_offset.min(
|
|
624
|
+
pub(crate) fn compute_location(index: &LineIndex, start_offset: usize, end_offset: usize) -> Location {
|
|
625
|
+
let start = start_offset.min(index.source_length());
|
|
626
|
+
let end = end_offset.min(index.source_length());
|
|
544
627
|
|
|
545
628
|
Location {
|
|
546
629
|
start_offset: start,
|
|
547
630
|
end_offset: end,
|
|
548
|
-
start_line: line_at(
|
|
549
|
-
start_column: column_at(
|
|
550
|
-
end_line: line_at(
|
|
551
|
-
end_column: column_at(
|
|
631
|
+
start_line: index.line_at(start),
|
|
632
|
+
start_column: index.column_at(start),
|
|
633
|
+
end_line: index.line_at(end),
|
|
634
|
+
end_column: index.column_at(end),
|
|
635
|
+
start_character_column: index.character_column_at(start),
|
|
636
|
+
end_character_column: index.character_column_at(end),
|
|
552
637
|
}
|
|
553
638
|
}
|
|
554
639
|
|
|
@@ -635,25 +720,17 @@ pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> yaml_serde::Value {
|
|
|
635
720
|
}
|
|
636
721
|
|
|
637
722
|
if let Some(scalar) = extract_scalar(node) {
|
|
638
|
-
use crate::syntax::{
|
|
639
|
-
|
|
640
|
-
return match
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
YerbaValueType::Float => scalar
|
|
651
|
-
.text
|
|
652
|
-
.parse::<f64>()
|
|
653
|
-
.map(|n| yaml_serde::Value::Number(yaml_serde::Number::from(n)))
|
|
654
|
-
.unwrap_or(yaml_serde::Value::String(scalar.text)),
|
|
655
|
-
|
|
656
|
-
YerbaValueType::String => yaml_serde::Value::String(scalar.text),
|
|
723
|
+
use crate::syntax::{resolve_scalar, PlainScalar};
|
|
724
|
+
|
|
725
|
+
return match resolve_scalar(&scalar) {
|
|
726
|
+
PlainScalar::Null => yaml_serde::Value::Null,
|
|
727
|
+
PlainScalar::Boolean(boolean) => yaml_serde::Value::Bool(boolean),
|
|
728
|
+
PlainScalar::Integer(integer) => yaml_serde::Value::Number(match i64::try_from(integer) {
|
|
729
|
+
Ok(integer) => yaml_serde::Number::from(integer),
|
|
730
|
+
Err(_) => yaml_serde::Number::from(integer as u64),
|
|
731
|
+
}),
|
|
732
|
+
PlainScalar::Float(float) => yaml_serde::Value::Number(yaml_serde::Number::from(float)),
|
|
733
|
+
PlainScalar::String => yaml_serde::Value::String(scalar.text),
|
|
657
734
|
};
|
|
658
735
|
}
|
|
659
736
|
|
|
@@ -728,6 +805,26 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
728
805
|
}
|
|
729
806
|
}
|
|
730
807
|
|
|
808
|
+
SelectorSegment::IndexFromEnd(_) => {
|
|
809
|
+
if let Some(sequence) = find_block_sequence(node) {
|
|
810
|
+
segment
|
|
811
|
+
.sequence_index(sequence.entries().count())
|
|
812
|
+
.and_then(|index| sequence.entries().nth(index))
|
|
813
|
+
.map(|entry| vec![entry.syntax().clone()])
|
|
814
|
+
.unwrap_or_default()
|
|
815
|
+
} else if let Some(sequence) = find_flow_sequence(node) {
|
|
816
|
+
let entries = flow_sequence_entries(&sequence);
|
|
817
|
+
|
|
818
|
+
segment
|
|
819
|
+
.sequence_index(entries.len())
|
|
820
|
+
.and_then(|index| entries.into_iter().nth(index))
|
|
821
|
+
.map(|entry| vec![entry])
|
|
822
|
+
.unwrap_or_default()
|
|
823
|
+
} else {
|
|
824
|
+
Vec::new()
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
731
828
|
SelectorSegment::AllKeys => {
|
|
732
829
|
if let Some(map) = find_block_map(node) {
|
|
733
830
|
map.entries().filter_map(|entry| entry.value().map(|value| value.syntax().clone())).collect()
|
|
@@ -765,8 +862,8 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
765
862
|
}
|
|
766
863
|
}
|
|
767
864
|
|
|
768
|
-
pub(crate) fn navigate_from_node(node: &SyntaxNode,
|
|
769
|
-
let parsed = crate::selector::Selector::parse(
|
|
865
|
+
pub(crate) fn navigate_from_node(node: &SyntaxNode, selector: &str) -> Vec<SyntaxNode> {
|
|
866
|
+
let parsed = crate::selector::Selector::parse(selector);
|
|
770
867
|
|
|
771
868
|
navigate_remaining(node, parsed.segments())
|
|
772
869
|
}
|
|
@@ -775,6 +872,7 @@ pub(crate) fn navigate_from_node(node: &SyntaxNode, path: &str) -> Vec<SyntaxNod
|
|
|
775
872
|
pub(crate) struct EntryGroup {
|
|
776
873
|
pub(crate) separator: String,
|
|
777
874
|
pub(crate) preceding: String,
|
|
875
|
+
pub(crate) gap: String,
|
|
778
876
|
pub(crate) body: String,
|
|
779
877
|
}
|
|
780
878
|
|
|
@@ -783,7 +881,7 @@ impl EntryGroup {
|
|
|
783
881
|
if self.preceding.is_empty() {
|
|
784
882
|
self.body.clone()
|
|
785
883
|
} else {
|
|
786
|
-
format!("{}
|
|
884
|
+
format!("{}{}{}", self.preceding, self.gap, self.body)
|
|
787
885
|
}
|
|
788
886
|
}
|
|
789
887
|
}
|
|
@@ -818,15 +916,17 @@ fn collect_entry_groups(parent: &SyntaxNode) -> Vec<EntryGroup> {
|
|
|
818
916
|
let entry_text = child.as_node().unwrap().text().to_string();
|
|
819
917
|
|
|
820
918
|
if groups.is_empty() {
|
|
821
|
-
let preceding = buffer.trim_start_matches('\n')
|
|
919
|
+
let (preceding, gap) = split_gap(buffer.trim_start_matches('\n'));
|
|
822
920
|
|
|
823
921
|
groups.push(EntryGroup {
|
|
824
922
|
separator: String::new(),
|
|
825
923
|
preceding,
|
|
924
|
+
gap,
|
|
826
925
|
body: entry_text,
|
|
827
926
|
});
|
|
828
927
|
} else {
|
|
829
928
|
let (trailing, separator, preceding) = split_at_blank_line(&buffer);
|
|
929
|
+
let (preceding, gap) = split_gap(&preceding);
|
|
830
930
|
|
|
831
931
|
if let Some(last) = groups.last_mut() {
|
|
832
932
|
last.body.push_str(&trailing);
|
|
@@ -835,6 +935,7 @@ fn collect_entry_groups(parent: &SyntaxNode) -> Vec<EntryGroup> {
|
|
|
835
935
|
groups.push(EntryGroup {
|
|
836
936
|
separator,
|
|
837
937
|
preceding,
|
|
938
|
+
gap,
|
|
838
939
|
body: entry_text,
|
|
839
940
|
});
|
|
840
941
|
}
|
|
@@ -873,7 +974,7 @@ fn split_at_blank_line(text: &str) -> (String, String, String) {
|
|
|
873
974
|
let rest = &text[position..];
|
|
874
975
|
let content_start = rest.len() - rest.trim_start_matches('\n').len();
|
|
875
976
|
let separator = rest[..content_start].to_string();
|
|
876
|
-
let preceding = rest[content_start..].
|
|
977
|
+
let preceding = rest[content_start..].to_string();
|
|
877
978
|
|
|
878
979
|
(trailing, separator, preceding)
|
|
879
980
|
} else {
|
|
@@ -881,8 +982,16 @@ fn split_at_blank_line(text: &str) -> (String, String, String) {
|
|
|
881
982
|
}
|
|
882
983
|
}
|
|
883
984
|
|
|
884
|
-
|
|
885
|
-
let
|
|
985
|
+
fn split_gap(text: &str) -> (String, String) {
|
|
986
|
+
let content = text.trim_end_matches(['\n', ' ', '\t']);
|
|
987
|
+
let newlines = text[content.len()..].matches('\n').count().max(1);
|
|
988
|
+
|
|
989
|
+
(content.to_string(), "\n".repeat(newlines))
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
pub(crate) fn collect_preceding_sibling_comments(parent: &SyntaxNode) -> (String, String, Option<rowan::TextSize>) {
|
|
993
|
+
let mut comments: Vec<(String, usize)> = Vec::new();
|
|
994
|
+
let mut newlines = 0;
|
|
886
995
|
let mut earliest_start = None;
|
|
887
996
|
let mut node = parent.clone();
|
|
888
997
|
|
|
@@ -893,10 +1002,11 @@ pub(crate) fn collect_preceding_sibling_comments(parent: &SyntaxNode) -> (String
|
|
|
893
1002
|
match element {
|
|
894
1003
|
rowan::NodeOrToken::Token(token) => {
|
|
895
1004
|
if token.kind() == SyntaxKind::COMMENT {
|
|
896
|
-
comments.push(token.text().to_string());
|
|
1005
|
+
comments.push((token.text().to_string(), newlines));
|
|
1006
|
+
newlines = 0;
|
|
897
1007
|
earliest_start = Some(token.text_range().start());
|
|
898
1008
|
} else if token.kind() == SyntaxKind::WHITESPACE {
|
|
899
|
-
|
|
1009
|
+
newlines += token.text().matches('\n').count();
|
|
900
1010
|
} else {
|
|
901
1011
|
break;
|
|
902
1012
|
}
|
|
@@ -910,7 +1020,7 @@ pub(crate) fn collect_preceding_sibling_comments(parent: &SyntaxNode) -> (String
|
|
|
910
1020
|
};
|
|
911
1021
|
}
|
|
912
1022
|
|
|
913
|
-
if !comments.is_empty() {
|
|
1023
|
+
if !comments.is_empty() || sibling.is_some() {
|
|
914
1024
|
break;
|
|
915
1025
|
}
|
|
916
1026
|
|
|
@@ -922,21 +1032,37 @@ pub(crate) fn collect_preceding_sibling_comments(parent: &SyntaxNode) -> (String
|
|
|
922
1032
|
}
|
|
923
1033
|
}
|
|
924
1034
|
|
|
1035
|
+
let gap = "\n".repeat(comments.first().map_or(1, |(_, newlines)| (*newlines).max(1)));
|
|
1036
|
+
|
|
925
1037
|
comments.reverse();
|
|
926
|
-
|
|
1038
|
+
|
|
1039
|
+
let text = comments
|
|
1040
|
+
.iter()
|
|
1041
|
+
.enumerate()
|
|
1042
|
+
.map(|(index, (comment, newlines))| {
|
|
1043
|
+
if index + 1 < comments.len() {
|
|
1044
|
+
format!("{}{}", comment, "\n".repeat((*newlines).max(1)))
|
|
1045
|
+
} else {
|
|
1046
|
+
comment.clone()
|
|
1047
|
+
}
|
|
1048
|
+
})
|
|
1049
|
+
.collect();
|
|
1050
|
+
|
|
1051
|
+
(text, gap, earliest_start)
|
|
927
1052
|
}
|
|
928
1053
|
|
|
929
1054
|
pub(crate) fn collect_groups_with_range(parent: &SyntaxNode) -> (Vec<EntryGroup>, TextRange) {
|
|
930
1055
|
let mut groups = collect_entry_groups(parent);
|
|
931
1056
|
|
|
932
|
-
let (sibling_comments, earliest_start) = collect_preceding_sibling_comments(parent);
|
|
1057
|
+
let (sibling_comments, sibling_gap, earliest_start) = collect_preceding_sibling_comments(parent);
|
|
933
1058
|
|
|
934
1059
|
if !sibling_comments.is_empty() {
|
|
935
1060
|
if let Some(first) = groups.first_mut() {
|
|
936
1061
|
if first.preceding.is_empty() {
|
|
937
1062
|
first.preceding = sibling_comments;
|
|
1063
|
+
first.gap = sibling_gap;
|
|
938
1064
|
} else {
|
|
939
|
-
first.preceding = format!("{}
|
|
1065
|
+
first.preceding = format!("{}{}{}", sibling_comments, sibling_gap, first.preceding);
|
|
940
1066
|
}
|
|
941
1067
|
}
|
|
942
1068
|
}
|
|
@@ -976,7 +1102,7 @@ pub(crate) fn rebuild_from_groups(groups: &[EntryGroup], indent: &str, preserve_
|
|
|
976
1102
|
if group.preceding.is_empty() {
|
|
977
1103
|
format!("{}{}{}", separator, indent, group.body)
|
|
978
1104
|
} else {
|
|
979
|
-
format!("{}{}
|
|
1105
|
+
format!("{}{}{}{}{}", separator, group.preceding, group.gap, indent, group.body)
|
|
980
1106
|
}
|
|
981
1107
|
}
|
|
982
1108
|
})
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
use super::*;
|
|
2
|
+
|
|
3
|
+
#[derive(Debug)]
|
|
4
|
+
pub struct NodeRecord {
|
|
5
|
+
pub parent: Option<usize>,
|
|
6
|
+
pub node_type: NodeType,
|
|
7
|
+
pub selector: String,
|
|
8
|
+
pub key_name: Option<String>,
|
|
9
|
+
pub scalar: Option<ScalarValue>,
|
|
10
|
+
pub has_value: bool,
|
|
11
|
+
pub location: Location,
|
|
12
|
+
pub key_location: Location,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fn location_of(lines: &LineIndex, range: TextRange) -> Location {
|
|
16
|
+
compute_location(lines, range.start().into(), range.end().into())
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fn collection_type(node: &SyntaxNode) -> Option<NodeType> {
|
|
20
|
+
let mut flow = None;
|
|
21
|
+
|
|
22
|
+
for descendant in node.descendants() {
|
|
23
|
+
match descendant.kind() {
|
|
24
|
+
SyntaxKind::BLOCK_SEQ => return Some(NodeType::Sequence),
|
|
25
|
+
SyntaxKind::BLOCK_MAP => return Some(NodeType::Map),
|
|
26
|
+
SyntaxKind::FLOW_SEQ if flow.is_none() => flow = Some(NodeType::Sequence),
|
|
27
|
+
SyntaxKind::FLOW_MAP if flow.is_none() => flow = Some(NodeType::Map),
|
|
28
|
+
_ => {}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
flow
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
struct Walker<'a> {
|
|
36
|
+
lines: &'a LineIndex,
|
|
37
|
+
records: Vec<NodeRecord>,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
impl Walker<'_> {
|
|
41
|
+
fn push(&mut self, parent: Option<usize>, node: Option<&SyntaxNode>, selector: String, key: Option<(String, Location)>) {
|
|
42
|
+
let collection = node.and_then(collection_type);
|
|
43
|
+
let scalar = match (node, collection) {
|
|
44
|
+
(Some(node), None) => extract_scalar(node),
|
|
45
|
+
_ => None,
|
|
46
|
+
};
|
|
47
|
+
let node_type = match (collection, &scalar) {
|
|
48
|
+
(Some(node_type), _) => node_type,
|
|
49
|
+
(None, Some(_)) => NodeType::Scalar,
|
|
50
|
+
(None, None) => NodeType::NotFound,
|
|
51
|
+
};
|
|
52
|
+
let location = match node {
|
|
53
|
+
Some(node) if parent.is_some() || !matches!(node_type, NodeType::Sequence) => location_of(self.lines, node.text_range()),
|
|
54
|
+
_ => Location::default(),
|
|
55
|
+
};
|
|
56
|
+
let (key_name, key_location) = key.map(|(name, location)| (Some(name), location)).unwrap_or((None, Location::default()));
|
|
57
|
+
|
|
58
|
+
let index = self.records.len();
|
|
59
|
+
|
|
60
|
+
self.records.push(NodeRecord {
|
|
61
|
+
parent,
|
|
62
|
+
node_type,
|
|
63
|
+
selector: selector.clone(),
|
|
64
|
+
key_name,
|
|
65
|
+
scalar,
|
|
66
|
+
has_value: true,
|
|
67
|
+
location,
|
|
68
|
+
key_location,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if let Some(node) = node {
|
|
72
|
+
match node_type {
|
|
73
|
+
NodeType::Sequence => self.sequence_children(index, node, &selector),
|
|
74
|
+
NodeType::Map => self.map_children(index, node, &selector),
|
|
75
|
+
_ => {}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn sequence_children(&mut self, index: usize, node: &SyntaxNode, selector: &str) {
|
|
81
|
+
let entries: Vec<SyntaxNode> = if let Some(sequence) = find_block_sequence(node) {
|
|
82
|
+
sequence.entries().map(|entry| entry.syntax().clone()).collect()
|
|
83
|
+
} else if let Some(sequence) = find_flow_sequence(node) {
|
|
84
|
+
flow_sequence_entries(&sequence)
|
|
85
|
+
} else {
|
|
86
|
+
Vec::new()
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
for (position, entry) in entries.iter().enumerate() {
|
|
90
|
+
self.push(Some(index), Some(entry), format!("{}[{}]", selector, position), None);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fn map_children(&mut self, index: usize, node: &SyntaxNode, selector: &str) {
|
|
95
|
+
let entries: Vec<(SyntaxNode, Option<SyntaxNode>, Option<SyntaxNode>)> = if let Some(map) = find_block_map(node) {
|
|
96
|
+
map
|
|
97
|
+
.entries()
|
|
98
|
+
.map(|entry| {
|
|
99
|
+
(
|
|
100
|
+
entry.syntax().clone(),
|
|
101
|
+
entry.key().map(|key| key.syntax().clone()),
|
|
102
|
+
entry.value().map(|value| value.syntax().clone()),
|
|
103
|
+
)
|
|
104
|
+
})
|
|
105
|
+
.collect()
|
|
106
|
+
} else if let Some(map) = find_flow_map(node) {
|
|
107
|
+
flow_map_entries(&map)
|
|
108
|
+
.into_iter()
|
|
109
|
+
.map(|entry| {
|
|
110
|
+
(
|
|
111
|
+
entry.syntax().clone(),
|
|
112
|
+
entry.key().map(|key| key.syntax().clone()),
|
|
113
|
+
entry.value().map(|value| value.syntax().clone()),
|
|
114
|
+
)
|
|
115
|
+
})
|
|
116
|
+
.collect()
|
|
117
|
+
} else {
|
|
118
|
+
Vec::new()
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
for (entry_node, key_node, value_node) in entries {
|
|
122
|
+
let Some(key_node) = key_node else { continue };
|
|
123
|
+
let Some(key_name) = extract_scalar_text(&key_node) else { continue };
|
|
124
|
+
|
|
125
|
+
let key_location = location_of(self.lines, key_node.text_range());
|
|
126
|
+
let child_selector = if selector.is_empty() {
|
|
127
|
+
key_name.clone()
|
|
128
|
+
} else {
|
|
129
|
+
format!("{}.{}", selector, key_name)
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
match value_node {
|
|
133
|
+
Some(value_node) => self.push(Some(index), Some(&value_node), child_selector, Some((key_name, key_location))),
|
|
134
|
+
None => self.records.push(NodeRecord {
|
|
135
|
+
parent: Some(index),
|
|
136
|
+
node_type: NodeType::Scalar,
|
|
137
|
+
selector: child_selector,
|
|
138
|
+
key_name: Some(key_name),
|
|
139
|
+
scalar: None,
|
|
140
|
+
has_value: false,
|
|
141
|
+
location: location_of(self.lines, entry_node.text_range()),
|
|
142
|
+
key_location,
|
|
143
|
+
}),
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
impl Document {
|
|
150
|
+
pub fn node_records(&self) -> Vec<NodeRecord> {
|
|
151
|
+
let mut walker = Walker {
|
|
152
|
+
lines: self.line_index(),
|
|
153
|
+
records: Vec::new(),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if let Some(document) = Root::cast(self.root.clone()).and_then(|root| root.documents().next()) {
|
|
157
|
+
walker.push(None, Some(document.syntax()), String::new(), None);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
walker.records
|
|
161
|
+
}
|
|
162
|
+
}
|