Fix YAML auto indent

YAML indents queries are tweaked to fix auto indent behavior.

A new capture type `indent.always` is introduced to address use cases
where combining indent captures on a single line is desired.

Fixes #6661
main
Skyler Hawthorne 1 year ago committed by Blaž Hrastnik
parent 57f093d836
commit 7078e84007

@ -237,38 +237,58 @@ fn get_first_in_line(mut node: Node, new_line_byte_pos: Option<usize>) -> Vec<bo
/// This is usually constructed in one of 2 ways: /// This is usually constructed in one of 2 ways:
/// - Successively add indent captures to get the (added) indent from a single line /// - Successively add indent captures to get the (added) indent from a single line
/// - Successively add the indent results for each line /// - Successively add the indent results for each line
#[derive(Default)] #[derive(Default, Debug, PartialEq, Eq, Clone)]
pub struct Indentation { pub struct Indentation {
/// The total indent (the number of indent levels) is defined as max(0, indent-outdent). /// The total indent (the number of indent levels) is defined as max(0, indent-outdent).
/// The string that this results in depends on the indent style (spaces or tabs, etc.) /// The string that this results in depends on the indent style (spaces or tabs, etc.)
indent: usize, indent: usize,
indent_always: usize,
outdent: usize, outdent: usize,
outdent_always: usize,
} }
impl Indentation { impl Indentation {
/// Add some other [Indentation] to this. /// Add some other [Indentation] to this.
/// The added indent should be the total added indent from one line /// The added indent should be the total added indent from one line
fn add_line(&mut self, added: &Indentation) { fn add_line(&mut self, added: &Indentation) {
if added.indent > 0 && added.outdent == 0 { self.indent += added.indent;
self.indent += 1; self.indent_always += added.indent_always;
} else if added.outdent > 0 && added.indent == 0 { self.outdent += added.outdent;
self.outdent += 1; self.outdent_always += added.outdent_always;
}
} }
/// Add an indent capture to this indent. /// Add an indent capture to this indent.
/// All the captures that are added in this way should be on the same line. /// All the captures that are added in this way should be on the same line.
fn add_capture(&mut self, added: IndentCaptureType) { fn add_capture(&mut self, added: IndentCaptureType) {
match added { match added {
IndentCaptureType::Indent => { IndentCaptureType::Indent => {
self.indent = 1; if self.indent_always == 0 {
self.indent = 1;
}
}
IndentCaptureType::IndentAlways => {
// any time we encounter an `indent.always` on the same line, we
// want to cancel out all regular indents
self.indent_always += 1;
self.indent = 0;
} }
IndentCaptureType::Outdent => { IndentCaptureType::Outdent => {
self.outdent = 1; if self.outdent_always == 0 {
self.outdent = 1;
}
}
IndentCaptureType::OutdentAlways => {
self.outdent_always += 1;
self.outdent = 0;
} }
} }
} }
fn as_string(&self, indent_style: &IndentStyle) -> String { fn as_string(&self, indent_style: &IndentStyle) -> String {
let indent_level = if self.indent >= self.outdent { let indent = self.indent_always + self.indent;
self.indent - self.outdent let outdent = self.outdent_always + self.outdent;
let indent_level = if indent >= outdent {
indent - outdent
} else { } else {
log::warn!("Encountered more outdent than indent nodes while calculating indentation: {} outdent, {} indent", self.outdent, self.indent); log::warn!("Encountered more outdent than indent nodes while calculating indentation: {} outdent, {} indent", self.outdent, self.indent);
0 0
@ -278,27 +298,32 @@ impl Indentation {
} }
/// An indent definition which corresponds to a capture from the indent query /// An indent definition which corresponds to a capture from the indent query
#[derive(Debug)]
struct IndentCapture { struct IndentCapture {
capture_type: IndentCaptureType, capture_type: IndentCaptureType,
scope: IndentScope, scope: IndentScope,
} }
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
enum IndentCaptureType { enum IndentCaptureType {
Indent, Indent,
IndentAlways,
Outdent, Outdent,
OutdentAlways,
} }
impl IndentCaptureType { impl IndentCaptureType {
fn default_scope(&self) -> IndentScope { fn default_scope(&self) -> IndentScope {
match self { match self {
IndentCaptureType::Indent => IndentScope::Tail, IndentCaptureType::Indent | IndentCaptureType::IndentAlways => IndentScope::Tail,
IndentCaptureType::Outdent => IndentScope::All, IndentCaptureType::Outdent | IndentCaptureType::OutdentAlways => IndentScope::All,
} }
} }
} }
/// This defines which part of a node an [IndentCapture] applies to. /// This defines which part of a node an [IndentCapture] applies to.
/// Each [IndentCaptureType] has a default scope, but the scope can be changed /// Each [IndentCaptureType] has a default scope, but the scope can be changed
/// with `#set!` property declarations. /// with `#set!` property declarations.
#[derive(Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum IndentScope { enum IndentScope {
/// The indent applies to the whole node /// The indent applies to the whole node
All, All,
@ -308,6 +333,7 @@ enum IndentScope {
/// A capture from the indent query which does not define an indent but extends /// A capture from the indent query which does not define an indent but extends
/// the range of a node. This is used before the indent is calculated. /// the range of a node. This is used before the indent is calculated.
#[derive(Debug)]
enum ExtendCapture { enum ExtendCapture {
Extend, Extend,
PreventOnce, PreventOnce,
@ -316,6 +342,7 @@ enum ExtendCapture {
/// The result of running a tree-sitter indent query. This stores for /// The result of running a tree-sitter indent query. This stores for
/// each node (identified by its ID) the relevant captures (already filtered /// each node (identified by its ID) the relevant captures (already filtered
/// by predicates). /// by predicates).
#[derive(Debug)]
struct IndentQueryResult { struct IndentQueryResult {
indent_captures: HashMap<usize, Vec<IndentCapture>>, indent_captures: HashMap<usize, Vec<IndentCapture>>,
extend_captures: HashMap<usize, Vec<ExtendCapture>>, extend_captures: HashMap<usize, Vec<ExtendCapture>>,
@ -334,6 +361,33 @@ fn query_indents(
let mut indent_captures: HashMap<usize, Vec<IndentCapture>> = HashMap::new(); let mut indent_captures: HashMap<usize, Vec<IndentCapture>> = HashMap::new();
let mut extend_captures: HashMap<usize, Vec<ExtendCapture>> = HashMap::new(); let mut extend_captures: HashMap<usize, Vec<ExtendCapture>> = HashMap::new();
cursor.set_byte_range(range); cursor.set_byte_range(range);
let get_node_start_line = |node: Node| {
let mut node_line = node.start_position().row;
// Adjust for the new line that will be inserted
if let Some((line, byte)) = new_line_break {
if node_line == line && node.start_byte() >= byte {
node_line += 1;
}
}
node_line
};
let get_node_end_line = |node: Node| {
let mut node_line = node.end_position().row;
// Adjust for the new line that will be inserted
if let Some((line, byte)) = new_line_break {
if node_line == line && node.end_byte() < byte {
node_line += 1;
}
}
node_line
};
// Iterate over all captures from the query // Iterate over all captures from the query
for m in cursor.matches(query, syntax.tree().root_node(), RopeProvider(text)) { for m in cursor.matches(query, syntax.tree().root_node(), RopeProvider(text)) {
// Skip matches where not all custom predicates are fulfilled // Skip matches where not all custom predicates are fulfilled
@ -360,21 +414,13 @@ fn query_indents(
Some(QueryPredicateArg::Capture(capt1)), Some(QueryPredicateArg::Capture(capt1)),
Some(QueryPredicateArg::Capture(capt2)) Some(QueryPredicateArg::Capture(capt2))
) => { ) => {
let get_line_num = |node: Node| {
let mut node_line = node.start_position().row;
// Adjust for the new line that will be inserted
if let Some((line, byte)) = new_line_break {
if node_line==line && node.start_byte()>=byte {
node_line += 1;
}
}
node_line
};
let n1 = m.nodes_for_capture_index(*capt1).next(); let n1 = m.nodes_for_capture_index(*capt1).next();
let n2 = m.nodes_for_capture_index(*capt2).next(); let n2 = m.nodes_for_capture_index(*capt2).next();
match (n1, n2) { match (n1, n2) {
(Some(n1), Some(n2)) => { (Some(n1), Some(n2)) => {
let same_line = get_line_num(n1)==get_line_num(n2); let n1_line = get_node_start_line(n1);
let n2_line = get_node_start_line(n2);
let same_line = n1_line == n2_line;
same_line==(pred.operator.as_ref()=="same-line?") same_line==(pred.operator.as_ref()=="same-line?")
} }
_ => true, _ => true,
@ -385,6 +431,23 @@ fn query_indents(
} }
} }
} }
"one-line?" | "not-one-line?" => match pred.args.get(0) {
Some(QueryPredicateArg::Capture(capture_idx)) => {
let node = m.nodes_for_capture_index(*capture_idx).next();
match node {
Some(node) => {
let (start_line, end_line) = (get_node_start_line(node), get_node_end_line(node));
let one_line = end_line == start_line;
one_line != (pred.operator.as_ref() == "not-one-line?")
},
_ => true,
}
}
_ => {
panic!("Invalid indent query: Arguments to \"not-kind-eq?\" must be a capture and a string");
}
},
_ => { _ => {
panic!( panic!(
"Invalid indent query: Unknown predicate (\"{}\")", "Invalid indent query: Unknown predicate (\"{}\")",
@ -399,7 +462,9 @@ fn query_indents(
let capture_name = query.capture_names()[capture.index as usize].as_str(); let capture_name = query.capture_names()[capture.index as usize].as_str();
let capture_type = match capture_name { let capture_type = match capture_name {
"indent" => IndentCaptureType::Indent, "indent" => IndentCaptureType::Indent,
"indent.always" => IndentCaptureType::IndentAlways,
"outdent" => IndentCaptureType::Outdent, "outdent" => IndentCaptureType::Outdent,
"outdent.always" => IndentCaptureType::OutdentAlways,
"extend" => { "extend" => {
extend_captures extend_captures
.entry(capture.node.id()) .entry(capture.node.id())
@ -456,10 +521,15 @@ fn query_indents(
.push(indent_capture); .push(indent_capture);
} }
} }
IndentQueryResult {
let result = IndentQueryResult {
indent_captures, indent_captures,
extend_captures, extend_captures,
} };
log::trace!("indent result = {:?}", result);
result
} }
/// Handle extend queries. deepest_preceding is the deepest descendant of node that directly precedes the cursor position. /// Handle extend queries. deepest_preceding is the deepest descendant of node that directly precedes the cursor position.
@ -584,6 +654,7 @@ pub fn treesitter_indent_for_pos(
.tree() .tree()
.root_node() .root_node()
.descendant_for_byte_range(byte_pos, byte_pos)?; .descendant_for_byte_range(byte_pos, byte_pos)?;
let (query_result, deepest_preceding) = { let (query_result, deepest_preceding) = {
// The query range should intersect with all nodes directly preceding // The query range should intersect with all nodes directly preceding
// the position of the indent query in case one of them is extended. // the position of the indent query in case one of them is extended.
@ -642,10 +713,12 @@ pub fn treesitter_indent_for_pos(
// even if there are multiple "indent" nodes on the same line // even if there are multiple "indent" nodes on the same line
let mut indent_for_line = Indentation::default(); let mut indent_for_line = Indentation::default();
let mut indent_for_line_below = Indentation::default(); let mut indent_for_line_below = Indentation::default();
loop { loop {
// This can safely be unwrapped because `first_in_line` contains // This can safely be unwrapped because `first_in_line` contains
// one entry for each ancestor of the node (which is what we iterate over) // one entry for each ancestor of the node (which is what we iterate over)
let is_first = *first_in_line.last().unwrap(); let is_first = *first_in_line.last().unwrap();
// Apply all indent definitions for this node // Apply all indent definitions for this node
if let Some(definitions) = indent_captures.get(&node.id()) { if let Some(definitions) = indent_captures.get(&node.id()) {
for definition in definitions { for definition in definitions {
@ -667,6 +740,7 @@ pub fn treesitter_indent_for_pos(
if let Some(parent) = node.parent() { if let Some(parent) = node.parent() {
let mut node_line = node.start_position().row; let mut node_line = node.start_position().row;
let mut parent_line = parent.start_position().row; let mut parent_line = parent.start_position().row;
if node_line == line && new_line { if node_line == line && new_line {
// Also consider the line that will be inserted // Also consider the line that will be inserted
if node.start_byte() >= byte_pos { if node.start_byte() >= byte_pos {
@ -676,17 +750,20 @@ pub fn treesitter_indent_for_pos(
parent_line += 1; parent_line += 1;
} }
}; };
if node_line != parent_line { if node_line != parent_line {
// Don't add indent for the line below the line of the query
if node_line < line + (new_line as usize) { if node_line < line + (new_line as usize) {
// Don't add indent for the line below the line of the query
result.add_line(&indent_for_line_below); result.add_line(&indent_for_line_below);
} }
if node_line == parent_line + 1 { if node_line == parent_line + 1 {
indent_for_line_below = indent_for_line; indent_for_line_below = indent_for_line;
} else { } else {
result.add_line(&indent_for_line); result.add_line(&indent_for_line);
indent_for_line_below = Indentation::default(); indent_for_line_below = Indentation::default();
} }
indent_for_line = Indentation::default(); indent_for_line = Indentation::default();
} }
@ -700,6 +777,7 @@ pub fn treesitter_indent_for_pos(
{ {
result.add_line(&indent_for_line_below); result.add_line(&indent_for_line_below);
} }
result.add_line(&indent_for_line); result.add_line(&indent_for_line);
break; break;
} }
@ -810,4 +888,122 @@ mod test {
2 2
); );
} }
#[test]
fn add_capture() {
let indent = || Indentation {
indent: 1,
..Default::default()
};
let indent_always = || Indentation {
indent_always: 1,
..Default::default()
};
let outdent = || Indentation {
outdent: 1,
..Default::default()
};
let outdent_always = || Indentation {
outdent_always: 1,
..Default::default()
};
let add_capture = |mut indent: Indentation, capture| {
indent.add_capture(capture);
indent
};
// adding an indent to no indent makes an indent
assert_eq!(
indent(),
add_capture(Indentation::default(), IndentCaptureType::Indent)
);
assert_eq!(
indent_always(),
add_capture(Indentation::default(), IndentCaptureType::IndentAlways)
);
assert_eq!(
outdent(),
add_capture(Indentation::default(), IndentCaptureType::Outdent)
);
assert_eq!(
outdent_always(),
add_capture(Indentation::default(), IndentCaptureType::OutdentAlways)
);
// adding an indent to an already indented has no effect
assert_eq!(indent(), add_capture(indent(), IndentCaptureType::Indent));
assert_eq!(
outdent(),
add_capture(outdent(), IndentCaptureType::Outdent)
);
// adding an always to a regular makes it always
assert_eq!(
indent_always(),
add_capture(indent(), IndentCaptureType::IndentAlways)
);
assert_eq!(
outdent_always(),
add_capture(outdent(), IndentCaptureType::OutdentAlways)
);
// adding an always to an always is additive
assert_eq!(
Indentation {
indent_always: 2,
..Default::default()
},
add_capture(indent_always(), IndentCaptureType::IndentAlways)
);
assert_eq!(
Indentation {
outdent_always: 2,
..Default::default()
},
add_capture(outdent_always(), IndentCaptureType::OutdentAlways)
);
// adding regular to always should be associative
assert_eq!(
Indentation {
indent_always: 1,
..Default::default()
},
add_capture(
add_capture(indent(), IndentCaptureType::Indent),
IndentCaptureType::IndentAlways
)
);
assert_eq!(
Indentation {
indent_always: 1,
..Default::default()
},
add_capture(
add_capture(indent(), IndentCaptureType::IndentAlways),
IndentCaptureType::Indent
)
);
assert_eq!(
Indentation {
outdent_always: 1,
..Default::default()
},
add_capture(
add_capture(outdent(), IndentCaptureType::Outdent),
IndentCaptureType::OutdentAlways
)
);
assert_eq!(
Indentation {
outdent_always: 1,
..Default::default()
},
add_capture(
add_capture(outdent(), IndentCaptureType::OutdentAlways),
IndentCaptureType::Outdent
)
);
}
} }

@ -2999,6 +2999,7 @@ fn open(cx: &mut Context, open: Open) {
Open::Below => graphemes::prev_grapheme_boundary(text, range.to()), Open::Below => graphemes::prev_grapheme_boundary(text, range.to()),
Open::Above => range.from(), Open::Above => range.from(),
}); });
let new_line = match open { let new_line = match open {
// adjust position to the end of the line (next line - 1) // adjust position to the end of the line (next line - 1)
Open::Below => cursor_line + 1, Open::Below => cursor_line + 1,
@ -3006,13 +3007,15 @@ fn open(cx: &mut Context, open: Open) {
Open::Above => cursor_line, Open::Above => cursor_line,
}; };
let line_num = new_line.saturating_sub(1);
// Index to insert newlines after, as well as the char width // Index to insert newlines after, as well as the char width
// to use to compensate for those inserted newlines. // to use to compensate for those inserted newlines.
let (line_end_index, line_end_offset_width) = if new_line == 0 { let (line_end_index, line_end_offset_width) = if new_line == 0 {
(0, 0) (0, 0)
} else { } else {
( (
line_end_char_index(&doc.text().slice(..), new_line.saturating_sub(1)), line_end_char_index(&text, line_num),
doc.line_ending.len_chars(), doc.line_ending.len_chars(),
) )
}; };
@ -3023,10 +3026,11 @@ fn open(cx: &mut Context, open: Open) {
&doc.indent_style, &doc.indent_style,
doc.tab_width(), doc.tab_width(),
text, text,
new_line.saturating_sub(1), line_num,
line_end_index, line_end_index,
cursor_line, cursor_line,
); );
let indent_len = indent.len(); let indent_len = indent.len();
let mut text = String::with_capacity(1 + indent_len); let mut text = String::with_capacity(1 + indent_len);
text.push_str(doc.line_ending.as_str()); text.push_str(doc.line_ending.as_str());

@ -18,6 +18,7 @@ mod test {
mod auto_indent; mod auto_indent;
mod auto_pairs; mod auto_pairs;
mod commands; mod commands;
mod languages;
mod movement; mod movement;
mod picker; mod picker;
mod prompt; mod prompt;

@ -0,0 +1,41 @@
use super::*;
#[tokio::test(flavor = "multi_thread")]
async fn auto_indent() -> anyhow::Result<()> {
let app = || AppBuilder::new().with_file("foo.go", None);
let enter_tests = [
(
helpers::platform_line(indoc! {r##"
type Test struct {#[}|]#
"##}),
"i<ret>",
helpers::platform_line(indoc! {"\
type Test struct {
\t#[|\n]#
}
"}),
),
(
helpers::platform_line(indoc! {"\
func main() {
\tswitch nil {#[}|]#
}
"}),
"i<ret>",
helpers::platform_line(indoc! {"\
func main() {
\tswitch nil {
\t\t#[|\n]#
\t}
}
"}),
),
];
for test in enter_tests {
test_with_config(app(), test).await?;
}
Ok(())
}

@ -0,0 +1,4 @@
use super::*;
mod go;
mod yaml;

@ -0,0 +1,819 @@
use super::*;
#[tokio::test(flavor = "multi_thread")]
async fn auto_indent() -> anyhow::Result<()> {
let app = || AppBuilder::new().with_file("foo.yaml", None);
let below_tests = [
(
helpers::platform_line(indoc! {r##"
#[t|]#op:
baz: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"##}),
"o",
helpers::platform_line(indoc! {"\
top:
#[\n|]#
baz: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {r##"
top:
b#[a|]#z: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"##}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
#[\n|]#
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {r##"
top:
baz: foo
bazi#[:|]#
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"##}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
#[\n|]#
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {r##"
top:
baz: foo
bazi:
more: #[yes|]#
why: because
quux:
- 1
- 2
bax: foox
fook:
"##}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
#[\n|]#
why: because
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {r##"
top:
baz: foo
bazi:
more: yes
why: becaus#[e|]#
quux:
- 1
- 2
bax: foox
fook:
"##}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
#[\n|]#
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:#[\n|]#
- 1
- 2
bax: foox
fook:
"}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:
#[\n|]#
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:
- 1#[\n|]#
- 2
bax: foox
fook:
"}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:
- 1
#[\n|]#
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:#[\n|]#
"}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
#[\n|]#
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: |
some
multi
line
string#[\n|]#
fook:
"}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: |
some
multi
line
string
#[\n|]#
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: >
some
multi
line#[\n|]#
string
fook:
"}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: >
some
multi
line
#[\n|]#
string
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: >#[\n|]#
fook:
"}),
"o",
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: >
#[\n|]#
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
- top:#[\n|]#
baz: foo
bax: foox
fook:
"}),
"o",
helpers::platform_line(indoc! {"\
- top:
#[\n|]#
baz: foo
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
- top:
baz: foo#[\n|]#
bax: foox
fook:
"}),
"o",
helpers::platform_line(indoc! {"\
- top:
baz: foo
#[\n|]#
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
- top:
baz: foo
bax: foox#[\n|]#
fook:
"}),
"o",
helpers::platform_line(indoc! {"\
- top:
baz: foo
bax: foox
#[\n|]#
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz:
- one: two#[\n|]#
three: four
- top:
baz: foo
bax: foox
"}),
"o",
helpers::platform_line(indoc! {"\
top:
baz:
- one: two
#[\n|]#
three: four
- top:
baz: foo
bax: foox
"}),
),
// yaml map without a key
(
helpers::platform_line(indoc! {"\
top:#[\n|]#
"}),
"o",
helpers::platform_line(indoc! {"\
top:
#[\n|]#
"}),
),
(
helpers::platform_line(indoc! {"\
top#[:|]#
bottom: withvalue
"}),
"o",
helpers::platform_line(indoc! {"\
top:
#[\n|]#
bottom: withvalue
"}),
),
(
helpers::platform_line(indoc! {"\
bottom: withvalue
top#[:|]#
"}),
"o",
helpers::platform_line(indoc! {"\
bottom: withvalue
top:
#[\n|]#
"}),
),
];
for test in below_tests {
test_with_config(app(), test).await?;
}
let above_tests = [
(
helpers::platform_line(indoc! {r##"
#[t|]#op:
baz: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"##}),
"O",
helpers::platform_line(indoc! {"\
#[\n|]#
top:
baz: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {r##"
top:
b#[a|]#z: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"##}),
"O",
helpers::platform_line(indoc! {"\
top:
#[\n|]#
baz: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {r##"
top:
baz: foo
bazi#[:|]#
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"##}),
"O",
helpers::platform_line(indoc! {"\
top:
baz: foo
#[\n|]#
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {r##"
top:
baz: foo
bazi:
more: #[yes|]#
why: because
quux:
- 1
- 2
bax: foox
fook:
"##}),
"O",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
#[\n|]#
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {r##"
top:
baz: foo
bazi:
more: yes
why: becaus#[e|]#
quux:
- 1
- 2
bax: foox
fook:
"##}),
"O",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
#[\n|]#
why: because
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:#[\n|]#
- 1
- 2
bax: foox
fook:
"}),
"O",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
#[\n|]#
quux:
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:
- 1#[\n|]#
- 2
bax: foox
fook:
"}),
"O",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:
#[\n|]#
- 1
- 2
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
fook:#[\n|]#
"}),
"O",
helpers::platform_line(indoc! {"\
top:
baz: foo
bazi:
more: yes
why: because
quux:
- 1
- 2
bax: foox
#[\n|]#
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: |
some
multi
line
string#[\n|]#
fook:
"}),
"O",
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: |
some
multi
line
#[\n|]#
string
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: >
some#[\n|]#
multi
line
string
fook:
"}),
"O",
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: >
#[\n|]#
some
multi
line
string
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: >
fook:#[\n|]#
"}),
"O",
helpers::platform_line(indoc! {"\
top:
baz: foo
bax: >
#[\n|]#
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
- top:
baz: foo#[\n|]#
bax: foox
fook:
"}),
"O",
helpers::platform_line(indoc! {"\
- top:
#[\n|]#
baz: foo
bax: foox
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
- top:
baz: foo
bax: foox
fook:#[\n|]#
"}),
"O",
helpers::platform_line(indoc! {"\
- top:
baz: foo
bax: foox
#[\n|]#
fook:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
baz:
- one: two#[\n|]#
three: four
- top:
baz: foo
bax: foox
"}),
"O",
helpers::platform_line(indoc! {"\
top:
baz:
#[\n|]#
- one: two
three: four
- top:
baz: foo
bax: foox
"}),
),
// yaml map without a key
(
helpers::platform_line(indoc! {"\
top:#[\n|]#
"}),
"O",
helpers::platform_line(indoc! {"\
#[\n|]#
top:
"}),
),
(
helpers::platform_line(indoc! {"\
bottom: withvalue
top#[:|]#
"}),
"O",
helpers::platform_line(indoc! {"\
bottom: withvalue
#[\n|]#
top:
"}),
),
(
helpers::platform_line(indoc! {"\
top:
bottom:#[ |]#withvalue
"}),
"O",
helpers::platform_line(indoc! {"\
top:
#[\n|]#
bottom: withvalue
"}),
),
];
for test in above_tests {
test_with_config(app(), test).await?;
}
let enter_tests = [
(
helpers::platform_line(indoc! {r##"
foo: #[b|]#ar
"##}),
"i<ret>",
helpers::platform_line(indoc! {"\
foo:
#[|b]#ar
"}),
),
(
helpers::platform_line(indoc! {"\
foo:#[\n|]#
"}),
"i<ret>",
helpers::platform_line(indoc! {"\
foo:
#[|\n]#
"}),
),
];
for test in enter_tests {
test_with_config(app(), test).await?;
}
Ok(())
}

@ -1,2 +1,36 @@
(block_mapping_pair) @indent (block_scalar) @indent @extend
; indent sequence items only if they span more than one line, e.g.
;
; - foo:
; bar: baz
; - quux:
; bar: baz
;
; but not
;
; - foo
; - bar
; - baz
((block_sequence_item) @item @indent.always @extend
(#not-one-line? @item))
; map pair where without a key
;
; foo:
((block_mapping_pair
key: (_) @key
!value
) @indent.always @extend
)
; map pair where the key and value are on different lines
;
; foo:
; bar: baz
((block_mapping_pair
key: (_) @key
value: (_) @val
(#not-same-line? @key @val)
) @indent.always @extend
)
Loading…
Cancel
Save