@@ -237,38 +237,58 @@ fn get_first_in_line(mut node: Node, new_line_byte_pos: Option<usize>) -> Vec<bo
237/// This is usually constructed in one of 2 ways:
237/// This is usually constructed in one of 2 ways:
238/// - Successively add indent captures to get the (added) indent from a single line
238/// - Successively add indent captures to get the (added) indent from a single line
239/// - Successively add the indent results for each line
239/// - Successively add the indent results for each line
240−#[derive(Default)]
240+#[derive(Default, Debug, PartialEq, Eq, Clone)]
241pub struct Indentation {
241pub struct Indentation {
242 /// The total indent (the number of indent levels) is defined as max(0, indent-outdent).
242 /// The total indent (the number of indent levels) is defined as max(0, indent-outdent).
243 /// The string that this results in depends on the indent style (spaces or tabs, etc.)
243 /// The string that this results in depends on the indent style (spaces or tabs, etc.)
244 indent: usize,
244 indent: usize,
245+ indent_always: usize,
245 outdent: usize,
246 outdent: usize,
247+ outdent_always: usize,
247impl Indentation {
249impl Indentation {
248 /// Add some other [Indentation] to this.
250 /// Add some other [Indentation] to this.
249 /// The added indent should be the total added indent from one line
251 /// The added indent should be the total added indent from one line
250 fn add_line(&mut self, added: &Indentation) {
252 fn add_line(&mut self, added: &Indentation) {
251− if added.indent > 0 && added.outdent == 0 {
253+ self.indent += added.indent;
252− self.indent += 1;
254+ self.indent_always += added.indent_always;
253− } else if added.outdent > 0 && added.indent == 0 {
255+ self.outdent += added.outdent;
254− self.outdent += 1;
256+ self.outdent_always += added.outdent_always;
257 /// Add an indent capture to this indent.
259 /// Add an indent capture to this indent.
258 /// All the captures that are added in this way should be on the same line.
260 /// All the captures that are added in this way should be on the same line.
259 fn add_capture(&mut self, added: IndentCaptureType) {
261 fn add_capture(&mut self, added: IndentCaptureType) {
260 match added {
262 match added {
261 IndentCaptureType::Indent => {
263 IndentCaptureType::Indent => {
264+ if self.indent_always == 0 {
262− self.indent = 1;
265+ self.indent = 1;
268+ IndentCaptureType::IndentAlways => {
269+ // any time we encounter an `indent.always` on the same line, we
270+ // want to cancel out all regular indents
271+ self.indent_always += 1;
264 IndentCaptureType::Outdent => {
274 IndentCaptureType::Outdent => {
275+ if self.outdent_always == 0 {
265− self.outdent = 1;
276+ self.outdent = 1;
279+ IndentCaptureType::OutdentAlways => {
280+ self.outdent_always += 1;
269 fn as_string(&self, indent_style: &IndentStyle) -> String {
286 fn as_string(&self, indent_style: &IndentStyle) -> String {
287+ let indent = self.indent_always + self.indent;
288+ let outdent = self.outdent_always + self.outdent;
270− let indent_level = if self.indent >= self.outdent {
290+ let indent_level = if indent >= outdent {
271− self.indent - self.outdent
291+ indent - outdent
273 log::warn!("Encountered more outdent than indent nodes while calculating indentation: {} outdent, {} indent", self.outdent, self.indent);
293 log::warn!("Encountered more outdent than indent nodes while calculating indentation: {} outdent, {} indent", self.outdent, self.indent);
@@ -278,27 +298,32 @@ impl Indentation {
280/// An indent definition which corresponds to a capture from the indent query
300/// An indent definition which corresponds to a capture from the indent query
281struct IndentCapture {
302struct IndentCapture {
282 capture_type: IndentCaptureType,
303 capture_type: IndentCaptureType,
283 scope: IndentScope,
304 scope: IndentScope,
285−#[derive(Clone, Copy)]
307+#[derive(Debug, Clone, Copy, PartialEq)]
286enum IndentCaptureType {
308enum IndentCaptureType {
290impl IndentCaptureType {
315impl IndentCaptureType {
291 fn default_scope(&self) -> IndentScope {
316 fn default_scope(&self) -> IndentScope {
292 match self {
317 match self {
293− IndentCaptureType::Indent => IndentScope::Tail,
318+ IndentCaptureType::Indent | IndentCaptureType::IndentAlways => IndentScope::Tail,
294− IndentCaptureType::Outdent => IndentScope::All,
319+ IndentCaptureType::Outdent | IndentCaptureType::OutdentAlways => IndentScope::All,
298/// This defines which part of a node an [IndentCapture] applies to.
323/// This defines which part of a node an [IndentCapture] applies to.
299/// Each [IndentCaptureType] has a default scope, but the scope can be changed
324/// Each [IndentCaptureType] has a default scope, but the scope can be changed
300/// with `#set!` property declarations.
325/// with `#set!` property declarations.
301−#[derive(Clone, Copy)]
326+#[derive(Debug, Clone, Copy)]
302enum IndentScope {
327enum IndentScope {
303 /// The indent applies to the whole node
328 /// The indent applies to the whole node
@@ -308,6 +333,7 @@ enum IndentScope {
309/// A capture from the indent query which does not define an indent but extends
334/// A capture from the indent query which does not define an indent but extends
310/// the range of a node. This is used before the indent is calculated.
335/// the range of a node. This is used before the indent is calculated.
311enum ExtendCapture {
337enum ExtendCapture {
313 PreventOnce,
339 PreventOnce,
@@ -316,6 +342,7 @@ enum ExtendCapture {
316/// The result of running a tree-sitter indent query. This stores for
342/// The result of running a tree-sitter indent query. This stores for
317/// each node (identified by its ID) the relevant captures (already filtered
343/// each node (identified by its ID) the relevant captures (already filtered
318/// by predicates).
344/// by predicates).
319struct IndentQueryResult {
346struct IndentQueryResult {
320 indent_captures: HashMap<usize, Vec<IndentCapture>>,
347 indent_captures: HashMap<usize, Vec<IndentCapture>>,
321 extend_captures: HashMap<usize, Vec<ExtendCapture>>,
348 extend_captures: HashMap<usize, Vec<ExtendCapture>>,
@@ -334,6 +361,33 @@ fn query_indents(
334 let mut indent_captures: HashMap<usize, Vec<IndentCapture>> = HashMap::new();
361 let mut indent_captures: HashMap<usize, Vec<IndentCapture>> = HashMap::new();
335 let mut extend_captures: HashMap<usize, Vec<ExtendCapture>> = HashMap::new();
362 let mut extend_captures: HashMap<usize, Vec<ExtendCapture>> = HashMap::new();
336 cursor.set_byte_range(range);
363 cursor.set_byte_range(range);
365+ let get_node_start_line = |node: Node| {
366+ let mut node_line = node.start_position().row;
368+ // Adjust for the new line that will be inserted
369+ if let Some((line, byte)) = new_line_break {
370+ if node_line == line && node.start_byte() >= byte {
378+ let get_node_end_line = |node: Node| {
379+ let mut node_line = node.end_position().row;
381+ // Adjust for the new line that will be inserted
382+ if let Some((line, byte)) = new_line_break {
383+ if node_line == line && node.end_byte() < byte {
337 // Iterate over all captures from the query
391 // Iterate over all captures from the query
338 for m in cursor.matches(query, syntax.tree().root_node(), RopeProvider(text)) {
392 for m in cursor.matches(query, syntax.tree().root_node(), RopeProvider(text)) {
339 // Skip matches where not all custom predicates are fulfilled
393 // Skip matches where not all custom predicates are fulfilled
@@ -360,21 +414,13 @@ fn query_indents(
360 Some(QueryPredicateArg::Capture(capt1)),
414 Some(QueryPredicateArg::Capture(capt1)),
361 Some(QueryPredicateArg::Capture(capt2))
415 Some(QueryPredicateArg::Capture(capt2))
363− let get_line_num = |node: Node| {
364− let mut node_line = node.start_position().row;
365− // Adjust for the new line that will be inserted
366− if let Some((line, byte)) = new_line_break {
367− if node_line==line && node.start_byte()>=byte {
373 let n1 = m.nodes_for_capture_index(*capt1).next();
417 let n1 = m.nodes_for_capture_index(*capt1).next();
374 let n2 = m.nodes_for_capture_index(*capt2).next();
418 let n2 = m.nodes_for_capture_index(*capt2).next();
375 match (n1, n2) {
419 match (n1, n2) {
376 (Some(n1), Some(n2)) => {
420 (Some(n1), Some(n2)) => {
377− let same_line = get_line_num(n1)==get_line_num(n2);
421+ let n1_line = get_node_start_line(n1);
422+ let n2_line = get_node_start_line(n2);
423+ let same_line = n1_line == n2_line;
378 same_line==(pred.operator.as_ref()=="same-line?")
424 same_line==(pred.operator.as_ref()=="same-line?")
380 _ => true,
426 _ => true,
@@ -385,6 +431,23 @@ fn query_indents(
434+ "one-line?" | "not-one-line?" => match pred.args.get(0) {
435+ Some(QueryPredicateArg::Capture(capture_idx)) => {
436+ let node = m.nodes_for_capture_index(*capture_idx).next();
440+ let (start_line, end_line) = (get_node_start_line(node), get_node_end_line(node));
441+ let one_line = end_line == start_line;
442+ one_line != (pred.operator.as_ref() == "not-one-line?")
448+ panic!("Invalid indent query: Arguments to \"not-kind-eq?\" must be a capture and a string");
390 "Invalid indent query: Unknown predicate (\"{}\")",
453 "Invalid indent query: Unknown predicate (\"{}\")",
@@ -399,7 +462,9 @@ fn query_indents(
399 let capture_name = query.capture_names()[capture.index as usize].as_str();
462 let capture_name = query.capture_names()[capture.index as usize].as_str();
400 let capture_type = match capture_name {
463 let capture_type = match capture_name {
401 "indent" => IndentCaptureType::Indent,
464 "indent" => IndentCaptureType::Indent,
465+ "indent.always" => IndentCaptureType::IndentAlways,
402 "outdent" => IndentCaptureType::Outdent,
466 "outdent" => IndentCaptureType::Outdent,
467+ "outdent.always" => IndentCaptureType::OutdentAlways,
403 "extend" => {
468 "extend" => {
404 extend_captures
469 extend_captures
405 .entry(capture.node.id())
470 .entry(capture.node.id())
@@ -456,10 +521,15 @@ fn query_indents(
456 .push(indent_capture);
521 .push(indent_capture);
459− IndentQueryResult {
525+ let result = IndentQueryResult {
460 indent_captures,
526 indent_captures,
461 extend_captures,
527 extend_captures,
530+ log::trace!("indent result = {:?}", result);
465/// Handle extend queries. deepest_preceding is the deepest descendant of node that directly precedes the cursor position.
535/// 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(
585 .root_node()
655 .root_node()
586 .descendant_for_byte_range(byte_pos, byte_pos)?;
656 .descendant_for_byte_range(byte_pos, byte_pos)?;
587 let (query_result, deepest_preceding) = {
658 let (query_result, deepest_preceding) = {
588 // The query range should intersect with all nodes directly preceding
659 // The query range should intersect with all nodes directly preceding
589 // the position of the indent query in case one of them is extended.
660 // the position of the indent query in case one of them is extended.
@@ -642,10 +713,12 @@ pub fn treesitter_indent_for_pos(
642 // even if there are multiple "indent" nodes on the same line
713 // even if there are multiple "indent" nodes on the same line
643 let mut indent_for_line = Indentation::default();
714 let mut indent_for_line = Indentation::default();
644 let mut indent_for_line_below = Indentation::default();
715 let mut indent_for_line_below = Indentation::default();
646 // This can safely be unwrapped because `first_in_line` contains
718 // This can safely be unwrapped because `first_in_line` contains
647 // one entry for each ancestor of the node (which is what we iterate over)
719 // one entry for each ancestor of the node (which is what we iterate over)
648 let is_first = *first_in_line.last().unwrap();
720 let is_first = *first_in_line.last().unwrap();
649 // Apply all indent definitions for this node
722 // Apply all indent definitions for this node
650 if let Some(definitions) = indent_captures.get(&node.id()) {
723 if let Some(definitions) = indent_captures.get(&node.id()) {
651 for definition in definitions {
724 for definition in definitions {
@@ -667,6 +740,7 @@ pub fn treesitter_indent_for_pos(
667 if let Some(parent) = node.parent() {
740 if let Some(parent) = node.parent() {
668 let mut node_line = node.start_position().row;
741 let mut node_line = node.start_position().row;
669 let mut parent_line = parent.start_position().row;
742 let mut parent_line = parent.start_position().row;
670 if node_line == line && new_line {
744 if node_line == line && new_line {
671 // Also consider the line that will be inserted
745 // Also consider the line that will be inserted
672 if node.start_byte() >= byte_pos {
746 if node.start_byte() >= byte_pos {
@@ -676,17 +750,20 @@ pub fn treesitter_indent_for_pos(
676 parent_line += 1;
750 parent_line += 1;
679 if node_line != parent_line {
754 if node_line != parent_line {
755+ // Don't add indent for the line below the line of the query
680 if node_line < line + (new_line as usize) {
756 if node_line < line + (new_line as usize) {
681− // Don't add indent for the line below the line of the query
682 result.add_line(&indent_for_line_below);
757 result.add_line(&indent_for_line_below);
684 if node_line == parent_line + 1 {
760 if node_line == parent_line + 1 {
685 indent_for_line_below = indent_for_line;
761 indent_for_line_below = indent_for_line;
687 result.add_line(&indent_for_line);
763 result.add_line(&indent_for_line);
688 indent_for_line_below = Indentation::default();
764 indent_for_line_below = Indentation::default();
690 indent_for_line = Indentation::default();
767 indent_for_line = Indentation::default();
@@ -700,6 +777,7 @@ pub fn treesitter_indent_for_pos(
701 result.add_line(&indent_for_line_below);
778 result.add_line(&indent_for_line_below);
703 result.add_line(&indent_for_line);
781 result.add_line(&indent_for_line);
@@ -810,4 +888,122 @@ mod test {
894+ let indent = || Indentation {
896+ ..Default::default()
898+ let indent_always = || Indentation {
900+ ..Default::default()
902+ let outdent = || Indentation {
904+ ..Default::default()
906+ let outdent_always = || Indentation {
908+ ..Default::default()
911+ let add_capture = |mut indent: Indentation, capture| {
912+ indent.add_capture(capture);
916+ // adding an indent to no indent makes an indent
919+ add_capture(Indentation::default(), IndentCaptureType::Indent)
923+ add_capture(Indentation::default(), IndentCaptureType::IndentAlways)
927+ add_capture(Indentation::default(), IndentCaptureType::Outdent)
931+ add_capture(Indentation::default(), IndentCaptureType::OutdentAlways)
934+ // adding an indent to an already indented has no effect
935+ assert_eq!(indent(), add_capture(indent(), IndentCaptureType::Indent));
938+ add_capture(outdent(), IndentCaptureType::Outdent)
941+ // adding an always to a regular makes it always
944+ add_capture(indent(), IndentCaptureType::IndentAlways)
948+ add_capture(outdent(), IndentCaptureType::OutdentAlways)
951+ // adding an always to an always is additive
955+ ..Default::default()
957+ add_capture(indent_always(), IndentCaptureType::IndentAlways)
962+ ..Default::default()
964+ add_capture(outdent_always(), IndentCaptureType::OutdentAlways)
967+ // adding regular to always should be associative
971+ ..Default::default()
974+ add_capture(indent(), IndentCaptureType::Indent),
975+ IndentCaptureType::IndentAlways
981+ ..Default::default()
984+ add_capture(indent(), IndentCaptureType::IndentAlways),
985+ IndentCaptureType::Indent
991+ ..Default::default()
994+ add_capture(outdent(), IndentCaptureType::Outdent),
995+ IndentCaptureType::OutdentAlways
1001+ ..Default::default()
1004+ add_capture(outdent(), IndentCaptureType::OutdentAlways),
1005+ IndentCaptureType::Outdent