@@ -1,6 +1,6 @@
1use crate::{
1use crate::{
2 find_first_non_whitespace_char,
2 find_first_non_whitespace_char,
3− syntax::Syntax,
3+ syntax::{IndentQuery, LanguageConfiguration, Syntax},
4 tree_sitter::{Node, Tree},
4 tree_sitter::{Node, Tree},
5 Rope, RopeSlice,
5 Rope, RopeSlice,
@@ -43,40 +43,11 @@ fn get_highest_syntax_node_at_bytepos(syntax: &Syntax, pos: usize) -> Option<Nod
43 Some(node)
43 Some(node)
46−fn calculate_indentation(node: Option<Node>, newline: bool) -> usize {
46+fn calculate_indentation(query: &IndentQuery, node: Option<Node>, newline: bool) -> usize {
47− let mut increment: i32 = 0;
47+ // NOTE: can't use contains() on query because of comparing Vec<String> and &str
48−
48+ // https://doc.rust-lang.org/std/vec/struct.Vec.html#method.contains
49− // Hardcoded for rust for now
50− let indent_scopes = &[
56− // "match_expression",
60− // indent_except_first_scopes
67− "field_declaration_list",
68− "field_initializer_list",
73− // "closure_expression",
79− let outdent = &["where", "}", "]", ")"];
50+ let mut increment: i32 = 0;
81 let mut node = match node {
52 let mut node = match node {
82 Some(node) => node,
53 Some(node) => node,
@@ -88,7 +59,7 @@ fn calculate_indentation(node: Option<Node>, newline: bool) -> usize {
88 // if we're calculating indentation for a brand new line then the current node will become the
59 // if we're calculating indentation for a brand new line then the current node will become the
89 // parent node. We need to take it's indentation level into account too.
60 // parent node. We need to take it's indentation level into account too.
90 let node_kind = node.kind();
61 let node_kind = node.kind();
91− if newline && indent_scopes.contains(&node_kind) {
62+ if newline && query.indent.contains(node_kind) {
92 increment += 1;
63 increment += 1;
@@ -102,14 +73,14 @@ fn calculate_indentation(node: Option<Node>, newline: bool) -> usize {
102 // }) <-- }) is two scopes
73 // }) <-- }) is two scopes
103 let starts_same_line = start == prev_start;
74 let starts_same_line = start == prev_start;
105− if outdent.contains(&node.kind()) && !starts_same_line {
76+ if query.outdent.contains(node.kind()) && !starts_same_line {
106 // we outdent by skipping the rules for the current level and jumping up
77 // we outdent by skipping the rules for the current level and jumping up
107 // node = parent;
78 // node = parent;
108 increment -= 1;
79 increment -= 1;
109 // continue;
80 // continue;
112− if indent_scopes.contains(&parent_kind) // && not_first_or_last_sibling
83+ if query.indent.contains(parent_kind) // && not_first_or_last_sibling
113 && !starts_same_line
84 && !starts_same_line
115 // println!("is_scope {}", parent_kind);
86 // println!("is_scope {}", parent_kind);
@@ -128,6 +99,7 @@ fn calculate_indentation(node: Option<Node>, newline: bool) -> usize {
130fn suggested_indent_for_line(
101fn suggested_indent_for_line(
102+ language_config: &LanguageConfiguration,
131 syntax: Option<&Syntax>,
103 syntax: Option<&Syntax>,
132 text: RopeSlice,
104 text: RopeSlice,
133 line_num: usize,
105 line_num: usize,
@@ -137,7 +109,7 @@ fn suggested_indent_for_line(
137 let current = indent_level_for_line(line, tab_width);
109 let current = indent_level_for_line(line, tab_width);
139 if let Some(start) = find_first_non_whitespace_char(text, line_num) {
111 if let Some(start) = find_first_non_whitespace_char(text, line_num) {
140− return suggested_indent_for_pos(syntax, text, start, false);
112+ return suggested_indent_for_pos(Some(language_config), syntax, text, start, false);
143 // if the line is blank, indent should be zero
115 // if the line is blank, indent should be zero
@@ -148,18 +120,24 @@ fn suggested_indent_for_line(
148// - it should return 0 when mass indenting stuff
120// - it should return 0 when mass indenting stuff
149// - it should look up the wrapper node and count it too when we press o/O
121// - it should look up the wrapper node and count it too when we press o/O
150pub fn suggested_indent_for_pos(
122pub fn suggested_indent_for_pos(
123+ language_config: Option<&LanguageConfiguration>,
151 syntax: Option<&Syntax>,
124 syntax: Option<&Syntax>,
152 text: RopeSlice,
125 text: RopeSlice,
153 pos: usize,
126 pos: usize,
154 new_line: bool,
127 new_line: bool,
155) -> usize {
128) -> usize {
156− if let Some(syntax) = syntax {
129+ if let (Some(query), Some(syntax)) = (
130+ language_config.and_then(|config| config.indent_query()),
157 let byte_start = text.char_to_byte(pos);
133 let byte_start = text.char_to_byte(pos);
158 let node = get_highest_syntax_node_at_bytepos(syntax, byte_start);
134 let node = get_highest_syntax_node_at_bytepos(syntax, byte_start);
136+ // let config = load indentation query config from Syntax(should contain language_config)
160 // TODO: special case for comments
138 // TODO: special case for comments
161 // TODO: if preserve_leading_whitespace
139 // TODO: if preserve_leading_whitespace
162− calculate_indentation(node, new_line)
140+ calculate_indentation(query, node, new_line)
164 // TODO: heuristics for non-tree sitter grammars
142 // TODO: heuristics for non-tree sitter grammars
@@ -286,6 +264,7 @@ where
286 tab_width: 4,
264 tab_width: 4,
287 unit: String::from(" "),
265 unit: String::from(" "),
267+ indent_query: OnceCell::new(),
@@ -304,7 +283,7 @@ where
304 let line = text.line(i);
283 let line = text.line(i);
305 let indent = indent_level_for_line(line, tab_width);
284 let indent = indent_level_for_line(line, tab_width);
306 assert_eq!(
285 assert_eq!(
307− suggested_indent_for_line(Some(&syntax), text, i, tab_width),
286+ suggested_indent_for_line(&language_config, Some(&syntax), text, i, tab_width),
309 "line {}: {}",
288 "line {}: {}",