From c06e8d8be696173f2347ec2d6dee2b59d43724f2 Mon Sep 17 00:00:00 2001 From: Axel Kappel <69117984+Kl4rry@users.noreply.github.com> Date: Thu, 20 Jun 2024 11:09:59 +0200 Subject: [PATCH] WIP --- crates/ferrite-core/src/language.rs | 32 +++++ crates/ferrite-core/src/language/syntax.rs | 5 + queries/rust/indents.scm | 148 +++++++++++++++++++++ test.html | 19 +++ 4 files changed, 204 insertions(+) create mode 100644 queries/rust/indents.scm create mode 100644 test.html diff --git a/crates/ferrite-core/src/language.rs b/crates/ferrite-core/src/language.rs index eb4a682..4ea029d 100644 --- a/crates/ferrite-core/src/language.rs +++ b/crates/ferrite-core/src/language.rs @@ -20,6 +20,7 @@ impl TreeSitterConfig { highlight_query: &str, injection_query: &str, locals_query: &str, + indent_query: &str, ) -> Self { Self { name: name.into(), @@ -29,6 +30,7 @@ impl TreeSitterConfig { highlight_query, injection_query, locals_query, + indent_query, ) .unwrap(), ), @@ -111,6 +113,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/rust/highlights.scm"), include_str!("../../../queries/rust/injections.scm"), include_str!("../../../queries/rust/locals.scm"), + include_str!("../../../queries/rust/indents.scm"), ), #[cfg(feature = "lang-json")] "json" => TreeSitterConfig::new( @@ -119,6 +122,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/json/highlights.scm"), "", "", + "", ), #[cfg(feature = "lang-c")] "c" => TreeSitterConfig::new( @@ -127,6 +131,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/c/highlights.scm"), include_str!("../../../queries/c/injections.scm"), "", + "", ), #[cfg(feature = "lang-cpp")] "cpp" => TreeSitterConfig::new( @@ -135,6 +140,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/cpp/highlights.scm"), include_str!("../../../queries/cpp/injections.scm"), "", + "", ), #[cfg(feature = "lang-cmake")] "cmake" => TreeSitterConfig::new( @@ -143,6 +149,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/cmake/highlights.scm"), include_str!("../../../queries/cmake/injections.scm"), "", + "", ), #[cfg(feature = "lang-css")] "css" => TreeSitterConfig::new( @@ -151,6 +158,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/css/highlights.scm"), include_str!("../../../queries/css/injections.scm"), "", + "", ), #[cfg(feature = "lang-glsl")] "glsl" => TreeSitterConfig::new( @@ -159,6 +167,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/glsl/highlights.scm"), "", "", + "", ), #[cfg(feature = "lang-html")] "html" => TreeSitterConfig::new( @@ -167,6 +176,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/html/highlights.scm"), include_str!("../../../queries/html/injections.scm"), "", + "", ), #[cfg(feature = "lang-md")] "markdown" => TreeSitterConfig::new( @@ -175,6 +185,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/markdown/highlights.scm"), include_str!("../../../queries/markdown/injections.scm"), "", + "", ), #[cfg(feature = "lang-python")] "python" => TreeSitterConfig::new( @@ -183,6 +194,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/python/highlights.scm"), include_str!("../../../queries/python/injections.scm"), include_str!("../../../queries/python/locals.scm"), + "", ), #[cfg(feature = "lang-toml")] "toml" => TreeSitterConfig::new( @@ -191,6 +203,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/toml/highlights.scm"), include_str!("../../../queries/toml/injections.scm"), "", + "", ), #[cfg(feature = "lang-xml")] "xml" => TreeSitterConfig::new( @@ -199,6 +212,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/xml/highlights.scm"), include_str!("../../../queries/xml/injections.scm"), "", + "", ), #[cfg(feature = "lang-yaml")] "yaml" => TreeSitterConfig::new( @@ -207,6 +221,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/yaml/highlights.scm"), include_str!("../../../queries/yaml/injections.scm"), "", + "", ), #[cfg(feature = "lang-c-sharp")] "c-sharp" => TreeSitterConfig::new( @@ -215,6 +230,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/c-sharp/highlights.scm"), include_str!("../../../queries/c-sharp/injections.scm"), "", + "", ), #[cfg(feature = "lang-bash")] "bash" => TreeSitterConfig::new( @@ -223,6 +239,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/bash/highlights.scm"), include_str!("../../../queries/bash/injections.scm"), "", + "", ), #[cfg(feature = "lang-fish")] "fish" => TreeSitterConfig::new( @@ -231,6 +248,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/fish/highlights.scm"), include_str!("../../../queries/fish/injections.scm"), "", + "", ), #[cfg(feature = "lang-comment")] "comment" => TreeSitterConfig::new( @@ -239,6 +257,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/comment/highlights.scm"), "", "", + "", ), #[cfg(feature = "lang-javascript")] "javascript" => TreeSitterConfig::new( @@ -247,6 +266,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/javascript/highlights.scm"), include_str!("../../../queries/javascript/injections.scm"), include_str!("../../../queries/javascript/locals.scm"), + "", ), #[cfg(feature = "lang-ron")] "ron" => TreeSitterConfig::new( @@ -255,6 +275,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/ron/highlights.scm"), include_str!("../../../queries/ron/injections.scm"), "", + "", ), #[cfg(feature = "lang-fortran")] "fortran" => TreeSitterConfig::new( @@ -263,6 +284,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/fortran/highlights.scm"), include_str!("../../../queries/fortran/injections.scm"), "", + "", ), #[cfg(feature = "lang-zig")] "zig" => TreeSitterConfig::new( @@ -271,6 +293,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/zig/highlights.scm"), include_str!("../../../queries/zig/injections.scm"), "", + "", ), #[cfg(feature = "lang-hyprlang")] "hyprlang" => TreeSitterConfig::new( @@ -279,6 +302,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/hyprlang/highlights.scm"), include_str!("../../../queries/hyprlang/injections.scm"), "", + "", ), #[cfg(feature = "lang-go")] "go" => TreeSitterConfig::new( @@ -287,6 +311,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/go/highlights.scm"), include_str!("../../../queries/go/injections.scm"), include_str!("../../../queries/go/locals.scm"), + "", ), #[cfg(feature = "lang-typescript")] "typescript" => TreeSitterConfig::new( @@ -295,6 +320,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/typescript/highlights.scm"), include_str!("../../../queries/typescript/injections.scm"), include_str!("../../../queries/typescript/locals.scm"), + "", ), #[cfg(feature = "lang-ini")] "ini" => TreeSitterConfig::new( @@ -303,6 +329,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/ini/highlights.scm"), "", "", + "", ), #[cfg(feature = "lang-diff")] "diff" => TreeSitterConfig::new( @@ -311,6 +338,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/diff/highlights.scm"), "", "", + "", ), #[cfg(feature = "lang-git-config")] "git-config" => TreeSitterConfig::new( @@ -319,6 +347,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/git-config/highlights.scm"), "", "", + "", ), #[cfg(feature = "lang-git-commit")] "git-commit" => TreeSitterConfig::new( @@ -327,6 +356,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/git-commit/highlights.scm"), include_str!("../../../queries/git-commit/injections.scm"), "", + "", ), #[cfg(feature = "lang-rebase")] "git-rebase" => TreeSitterConfig::new( @@ -335,6 +365,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/git-rebase/highlights.scm"), include_str!("../../../queries/git-rebase/injections.scm"), "", + "", ), #[cfg(feature = "lang-dockerfile")] "dockerfile" => TreeSitterConfig::new( @@ -343,6 +374,7 @@ fn get_lang_config(name: &str) -> Option { include_str!("../../../queries/dockerfile/highlights.scm"), include_str!("../../../queries/dockerfile/injections.scm"), "", + "", ), _ => return None, }) diff --git a/crates/ferrite-core/src/language/syntax.rs b/crates/ferrite-core/src/language/syntax.rs index b2bff97..0798e11 100644 --- a/crates/ferrite-core/src/language/syntax.rs +++ b/crates/ferrite-core/src/language/syntax.rs @@ -192,6 +192,7 @@ pub struct HighlightConfiguration { pub language: Language, pub query: &'static Query, combined_injections_query: &'static Option, + indent_query: &'static Query, locals_pattern_index: usize, highlights_pattern_index: usize, non_local_variable_patterns: Vec, @@ -321,6 +322,7 @@ impl HighlightConfiguration { highlights_query: &str, injection_query: &str, locals_query: &str, + indents_query: &str, ) -> Result { // Concatenate the query strings, keeping track of the start offset of each section. let mut query_source = String::new(); @@ -347,6 +349,8 @@ impl HighlightConfiguration { } } + let indent_query = Query::new(language, indents_query)?; + // Construct a separate query just for dealing with the 'combined injections'. // Disable the combined injection patterns in the main query. let mut combined_injections_query = Query::new(language, injection_query)?; @@ -401,6 +405,7 @@ impl HighlightConfiguration { language, query: Box::leak(Box::new(query)), combined_injections_query: Box::leak(Box::new(combined_injections_query)), + indent_query: Box::leak(Box::new(indent_query)), locals_pattern_index, highlights_pattern_index, non_local_variable_patterns, diff --git a/queries/rust/indents.scm b/queries/rust/indents.scm new file mode 100644 index 0000000..af2e05e --- /dev/null +++ b/queries/rust/indents.scm @@ -0,0 +1,148 @@ +[ + (use_list) + (block) + (match_block) + (arguments) + (parameters) + (declaration_list) + (field_declaration_list) + (field_initializer_list) + (struct_pattern) + (tuple_pattern) + (unit_expression) + (enum_variant_list) + (call_expression) + (binary_expression) + (field_expression) + (await_expression) + (tuple_expression) + (array_expression) + (where_clause) + (type_cast_expression) + + (token_tree) + (macro_definition) + (token_tree_pattern) + (token_repetition) +] @indent + +[ + "}" + "]" + ")" +] @outdent + +; Indent the right side of assignments. +; The #not-same-line? predicate is required to prevent an extra indent for e.g. +; an else-clause where the previous if-clause starts on the same line as the assignment. +(assignment_expression + . + (_) @expr-start + right: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +(compound_assignment_expr + . + (_) @expr-start + right: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +(let_declaration + "let" @expr-start + value: (_) @indent + alternative: (_)? @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +(let_condition + . + (_) @expr-start + value: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +(if_expression + . + (_) @expr-start + condition: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +(static_item + . + (_) @expr-start + value: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +(field_pattern + . + (_) @expr-start + pattern: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +; Indent type aliases that span multiple lines, similar to +; regular assignment expressions +(type_item + . + (_) @expr-start + type: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) + +; Some field expressions where the left part is a multiline expression are not +; indented by cargo fmt. +; Because this multiline expression might be nested in an arbitrary number of +; field expressions, this can only be matched using a Regex. +(field_expression + value: (_) @val + "." @outdent + ; Check whether the first line ends with `(`, `{` or `[` (up to whitespace). + (#match? @val "(\\A[^\\n\\r]+(\\(|\\{|\\[)[\\t ]*(\\n|\\r))") +) +; Same as above, but with an additional `call_expression`. This is required since otherwise +; the arguments of the function call won't be outdented. +(call_expression + function: (field_expression + value: (_) @val + "." @outdent + (#match? @val "(\\A[^\\n\\r]+(\\(|\\{|\\[)[\\t ]*(\\n|\\r))") + ) + arguments: (_) @outdent +) + + +; Indent if guards in patterns. +; Since the tree-sitter grammar doesn't create a node for the if expression, +; it's not possible to do this correctly in all cases. Indenting the tail of the +; whole pattern whenever it contains an `if` only fails if the `if` appears after +; the second line of the pattern (which should only rarely be the case) +(match_pattern + . + (_) @expr-start + "if" @pattern-guard + (#not-same-line? @expr-start @pattern-guard) +) @indent + +; Align closure parameters if they span more than one line +(closure_parameters + "|" + . + (_) @anchor + (_) @expr-end + . + (#not-same-line? @anchor @expr-end) +) @align + +(for_expression + "in" @in + . + (_) @indent + (#not-same-line? @in @indent) + (#set! "scope" "all") +) + diff --git a/test.html b/test.html new file mode 100644 index 0000000..b8b6547 --- /dev/null +++ b/test.html @@ -0,0 +1,19 @@ + + + +Page Title + + + + + +

My First Heading

+

My first paragraph.

+ + +