Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-core/tests/indent.rs')
-rw-r--r--helix-core/tests/indent.rs219
1 files changed, 27 insertions, 192 deletions
diff --git a/helix-core/tests/indent.rs b/helix-core/tests/indent.rs
index ab733f93..e1114f4a 100644
--- a/helix-core/tests/indent.rs
+++ b/helix-core/tests/indent.rs
@@ -1,230 +1,65 @@
use helix_core::{
- indent::{indent_level_for_line, treesitter_indent_for_pos, IndentStyle},
- syntax::{config::Configuration, Loader},
+ indent::{treesitter_indent_for_pos, IndentStyle},
+ syntax::Loader,
Syntax,
};
-use helix_stdx::rope::RopeSliceExt;
-use ropey::Rope;
-use std::{ops::Range, path::PathBuf, process::Command};
+use std::path::PathBuf;
#[test]
fn test_treesitter_indent_rust() {
- standard_treesitter_test("rust.rs", "source.rust");
+ test_treesitter_indent("rust.rs", "source.rust");
}
-
#[test]
-fn test_treesitter_indent_cpp() {
- standard_treesitter_test("cpp.cpp", "source.cpp");
+fn test_treesitter_indent_rust_2() {
+ test_treesitter_indent("indent.rs", "source.rust");
+ // TODO Use commands.rs as indentation test.
+ // Currently this fails because we can't align the parameters of a closure yet
+ // test_treesitter_indent("commands.rs", "source.rust");
}
-#[test]
-fn test_treesitter_indent_rust_helix() {
- // We pin a specific git revision to prevent unrelated changes from causing the indent tests to fail.
- // Ideally, someone updates this once in a while and fixes any errors that occur.
- let rev = "af382768cdaf89ff547dbd8f644a1bddd90e7c8f";
- let files = Command::new("git")
- .args([
- "ls-tree",
- "-r",
- "--name-only",
- "--full-tree",
- rev,
- "helix-term/src",
- ])
- .output()
- .unwrap();
- let files = String::from_utf8(files.stdout).unwrap();
-
- let ignored_files = [
- // Contains many macros that tree-sitter does not parse in a meaningful way and is otherwise not very interesting
- "helix-term/src/health.rs",
- ];
-
- for file in files.split_whitespace() {
- if ignored_files.contains(&file) {
- continue;
- }
- #[allow(clippy::single_range_in_vec_init)]
- let ignored_lines: Vec<Range<usize>> = match file {
- "helix-term/src/application.rs" => vec![
- // We can't handle complicated indent rules inside macros (`json!` in this case) since
- // the tree-sitter grammar only parses them as `token_tree` and `identifier` nodes.
- 1045..1051,
- ],
- "helix-term/src/commands.rs" => vec![
- // This is broken because of the current handling of `call_expression`
- // (i.e. having an indent query for it but outdenting again in specific cases).
- // The indent query is needed to correctly handle multi-line arguments in function calls
- // inside indented `field_expression` nodes (which occurs fairly often).
- //
- // Once we have the `@indent.always` capture type, it might be possible to just have an indent
- // capture for the `arguments` field of a call expression. That could enable us to correctly
- // handle this.
- 2226..2230,
- ],
- "helix-term/src/commands/dap.rs" => vec![
- // Complex `format!` macro
- 46..52,
- ],
- "helix-term/src/commands/lsp.rs" => vec![
- // Macro
- 624..627,
- // Return type declaration of a closure. `cargo fmt` adds an additional space here,
- // which we cannot (yet) model with our indent queries.
- 878..879,
- // Same as in `helix-term/src/commands.rs`
- 1335..1343,
- ],
- "helix-term/src/config.rs" => vec![
- // Multiline string
- 146..152,
- ],
- "helix-term/src/keymap.rs" => vec![
- // Complex macro (see above)
- 456..470,
- // Multiline string without indent
- 563..567,
- ],
- "helix-term/src/main.rs" => vec![
- // Multiline string
- 44..70,
- ],
- "helix-term/src/ui/completion.rs" => vec![
- // Macro
- 218..232,
- ],
- "helix-term/src/ui/editor.rs" => vec![
- // The chained function calls here are not indented, probably because of the comment
- // in between. Since `cargo fmt` doesn't even attempt to format it, there's probably
- // no point in trying to indent this correctly.
- 342..350,
- ],
- "helix-term/src/ui/lsp.rs" => vec![
- // Macro
- 56..61,
- ],
- "helix-term/src/ui/statusline.rs" => vec![
- // Same as in `helix-term/src/commands.rs`
- 436..442,
- 450..456,
- ],
- _ => Vec::new(),
- };
-
- let git_object = rev.to_string() + ":" + file;
- let content = Command::new("git")
- .args(["cat-file", "blob", &git_object])
- .output()
- .unwrap();
- let doc = Rope::from_reader(&mut content.stdout.as_slice()).unwrap();
- test_treesitter_indent(file, doc, "source.rust", ignored_lines);
- }
-}
-
-#[test]
-fn test_indent_level_for_line_with_spaces() {
- let tab_width: usize = 4;
- let indent_width: usize = 4;
-
- let line = ropey::Rope::from_str(" Indented with 8 spaces");
-
- let indent_level = indent_level_for_line(line.slice(0..), tab_width, indent_width);
- assert_eq!(indent_level, 2)
-}
-
-#[test]
-fn test_indent_level_for_line_with_tabs() {
- let tab_width: usize = 4;
- let indent_width: usize = 4;
-
- let line = ropey::Rope::from_str("\t\tIndented with 2 tabs");
-
- let indent_level = indent_level_for_line(line.slice(0..), tab_width, indent_width);
- assert_eq!(indent_level, 2)
-}
-
-#[test]
-fn test_indent_level_for_line_with_spaces_and_tabs() {
- let tab_width: usize = 4;
- let indent_width: usize = 4;
-
- let line = ropey::Rope::from_str(" \t \tIndented with mix of spaces and tabs");
-
- let indent_level = indent_level_for_line(line.slice(0..), tab_width, indent_width);
- assert_eq!(indent_level, 2)
-}
-
-fn indent_tests_dir() -> PathBuf {
+fn test_treesitter_indent(file_name: &str, lang_scope: &str) {
let mut test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
test_dir.push("tests/data/indent");
- test_dir
-}
-
-fn indent_test_path(name: &str) -> PathBuf {
- let mut path = indent_tests_dir();
- path.push(name);
- path
-}
-
-fn indent_tests_config() -> Configuration {
- let mut config_path = indent_tests_dir();
- config_path.push("languages.toml");
- let config = std::fs::read_to_string(config_path).unwrap();
- toml::from_str(&config).unwrap()
-}
-fn standard_treesitter_test(file_name: &str, lang_scope: &str) {
- let test_path = indent_test_path(file_name);
- let test_file = std::fs::File::open(test_path).unwrap();
+ let mut test_file = test_dir.clone();
+ test_file.push(file_name);
+ let test_file = std::fs::File::open(test_file).unwrap();
let doc = ropey::Rope::from_reader(test_file).unwrap();
- test_treesitter_indent(file_name, doc, lang_scope, Vec::new())
-}
-/// Test that all the lines in the given file are indented as expected.
-/// ignored_lines is a list of (1-indexed) line ranges that are excluded from this test.
-fn test_treesitter_indent(
- test_name: &str,
- doc: Rope,
- lang_scope: &str,
- ignored_lines: Vec<std::ops::Range<usize>>,
-) {
- let loader = Loader::new(indent_tests_config()).unwrap();
+ let mut config_file = test_dir;
+ config_file.push("languages.toml");
+ let config = std::fs::read(config_file).unwrap();
+ let config = toml::from_slice(&config).unwrap();
+ let loader = Loader::new(config);
// set runtime path so we can find the queries
let mut runtime = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
runtime.push("../runtime");
std::env::set_var("HELIX_RUNTIME", runtime.to_str().unwrap());
- let language = loader.language_for_scope(lang_scope).unwrap();
- let language_config = loader.language(language).config();
- let indent_style = IndentStyle::from_str(&language_config.indent.as_ref().unwrap().unit);
+ let language_config = loader.language_config_for_scope(lang_scope).unwrap();
+ let highlight_config = language_config.highlight_config(&[]).unwrap();
+ let syntax = Syntax::new(&doc, highlight_config, std::sync::Arc::new(loader));
+ let indent_query = language_config.indent_query().unwrap();
let text = doc.slice(..);
- let syntax = Syntax::new(text, language, &loader).unwrap();
- let indent_query = loader.indent_query(language).unwrap();
for i in 0..doc.len_lines() {
let line = text.line(i);
- if ignored_lines.iter().any(|range| range.contains(&(i + 1))) {
- continue;
- }
- if let Some(pos) = line.first_non_whitespace_char() {
- let tab_width: usize = 4;
+ if let Some(pos) = helix_core::find_first_non_whitespace_char(line) {
let suggested_indent = treesitter_indent_for_pos(
indent_query,
&syntax,
- tab_width,
- indent_style.indent_width(tab_width),
+ &IndentStyle::Spaces(4),
+ 4,
text,
i,
text.line_to_char(i) + pos,
false,
)
- .unwrap()
- .to_string(&indent_style, tab_width);
+ .unwrap();
assert!(
line.get_slice(..pos).map_or(false, |s| s == suggested_indent),
- "Wrong indentation for file {:?} on line {}:\n\"{}\" (original line)\n\"{}\" (suggested indentation)\n",
- test_name,
+ "Wrong indentation on line {}:\n\"{}\" (original line)\n\"{}\" (suggested indentation)\n",
i+1,
line.slice(..line.len_chars()-1),
suggested_indent,