use std::collections::HashSet;
use line_numbers::LinePositions;
use streaming_iterator::StreamingIterator as _;
use tree_sitter as ts;
use typed_arena::Arena;
use super::syntax::MatchedPos;
use super::syntax::{self, StringKind};
use crate::hash::DftHashMap;
use crate::options::DiffOptions;
use crate::parse::guess_language as guess;
use crate::parse::syntax::{AtomKind, Syntax};
pub(crate) struct TreeSitterSubLanguage {
query: ts::Query,
parse_as: guess::Language,
}
pub(crate) struct TreeSitterConfig {
pub(crate) language: ts::Language,
atom_nodes: HashSet<&'static str>,
delimiter_tokens: Vec<(&'static str, &'static str)>,
highlight_query: ts::Query,
sub_languages: Vec<TreeSitterSubLanguage>,
}
extern "C" {
fn tree_sitter_ada() -> ts::Language;
fn tree_sitter_apex() -> ts::Language;
fn tree_sitter_clojure() -> ts::Language;
fn tree_sitter_cmake() -> ts::Language;
fn tree_sitter_commonlisp() -> ts::Language;
fn tree_sitter_dart() -> ts::Language;
fn tree_sitter_devicetree() -> ts::Language;
fn tree_sitter_elisp() -> ts::Language;
fn tree_sitter_elixir() -> ts::Language;
fn tree_sitter_elm() -> ts::Language;
fn tree_sitter_elvish() -> ts::Language;
fn tree_sitter_erlang() -> ts::Language;
fn tree_sitter_fsharp() -> ts::Language;
fn tree_sitter_gleam() -> ts::Language;
fn tree_sitter_hare() -> ts::Language;
fn tree_sitter_hack() -> ts::Language;
fn tree_sitter_hcl() -> ts::Language;
fn tree_sitter_janet_simple() -> ts::Language;
fn tree_sitter_kotlin() -> ts::Language;
fn tree_sitter_latex() -> ts::Language;
fn tree_sitter_newick() -> ts::Language;
fn tree_sitter_nix() -> ts::Language;
fn tree_sitter_pascal() -> ts::Language;
fn tree_sitter_perl() -> ts::Language;
fn tree_sitter_qmljs() -> ts::Language;
fn tree_sitter_r() -> ts::Language;
fn tree_sitter_racket() -> ts::Language;
fn tree_sitter_rust() -> ts::Language;
fn tree_sitter_scheme() -> ts::Language;
fn tree_sitter_smali() -> ts::Language;
fn tree_sitter_scss() -> ts::Language;
fn tree_sitter_solidity() -> ts::Language;
fn tree_sitter_sql() -> ts::Language;
fn tree_sitter_swift() -> ts::Language;
fn tree_sitter_vhdl() -> ts::Language;
fn tree_sitter_zig() -> ts::Language;
}
const OCAML_ATOM_NODES: [&str; 6] = [
"character",
"string",
"quoted_string",
"tag",
"type_variable",
"attribute_id",
];
pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
use guess::Language::*;
match language {
Ada => {
let language = unsafe { tree_sitter_ada() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string_literal", "character_literal"]
.into_iter()
.collect(),
delimiter_tokens: vec![("(", ")"), ("[", "]")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/ada.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Apex => {
let language = unsafe { tree_sitter_apex() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![
"string_literal",
"null_literal",
"boolean",
"int",
"decimal_floating_point_literal",
"date_literal",
"currency_literal",
]
.into_iter()
.collect(),
delimiter_tokens: vec![("[", "]"), ("(", ")"), ("{", "}")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/apex.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Bash => {
let language_fn = tree_sitter_bash::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "raw_string", "heredoc_body"]
.into_iter()
.collect(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("[", "]")],
highlight_query: ts::Query::new(&language, tree_sitter_bash::HIGHLIGHT_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
C => {
let language_fn = tree_sitter_c::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string_literal", "char_literal"].into_iter().collect(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("[", "]")],
highlight_query: ts::Query::new(&language, tree_sitter_c::HIGHLIGHT_QUERY).unwrap(),
sub_languages: vec![],
}
}
CPlusPlus => {
let language_fn = tree_sitter_cpp::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let mut highlight_query = tree_sitter_c::HIGHLIGHT_QUERY.to_owned();
highlight_query.push_str(tree_sitter_cpp::HIGHLIGHT_QUERY);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string_literal", "char_literal"].into_iter().collect(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("[", "]"), ("<", ">")],
highlight_query: ts::Query::new(&language, &highlight_query).unwrap(),
sub_languages: vec![],
}
}
Clojure => {
let language = unsafe { tree_sitter_clojure() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["kwd_lit", "regex_lit"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]")]
.into_iter()
.collect(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/clojure.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
CMake => {
let language = unsafe { tree_sitter_cmake() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["argument"].into_iter().collect(),
delimiter_tokens: vec![("(", ")")].into_iter().collect(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/cmake.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
CommonLisp => {
let language = unsafe { tree_sitter_commonlisp() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["str_lit", "char_lit"].into_iter().collect(),
delimiter_tokens: vec![("(", ")")],
highlight_query: ts::Query::new(&language, "").unwrap(),
sub_languages: vec![],
}
}
CSharp => {
let language_fn = tree_sitter_c_sharp::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![
"string_literal",
"verbatim_string_literal",
"character_literal",
"modifier",
]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/c-sharp.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Css => {
let language_fn = tree_sitter_css::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![
"integer_value",
"float_value",
"color_value",
"string_value",
]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")")],
highlight_query: ts::Query::new(&language, tree_sitter_css::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Dart => {
let language = unsafe { tree_sitter_dart() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string_literal", "script_tag"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]"), ("<", ">")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/dart.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
DeviceTree => {
let language = unsafe { tree_sitter_devicetree() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["byte_string_literal", "string_literal"]
.into_iter()
.collect(),
delimiter_tokens: vec![("<", ">"), ("{", "}"), ("(", ")")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/devicetree.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
EmacsLisp => {
let language = unsafe { tree_sitter_elisp() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]")]
.into_iter()
.collect(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/elisp.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Elixir => {
let language = unsafe { tree_sitter_elixir() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "heredoc"].into_iter().collect(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("do", "end")]
.into_iter()
.collect(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/elixir.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Elm => {
let language = unsafe { tree_sitter_elm() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string_constant_expr"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]"), ("(", ")")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/elm.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Elvish => {
let language = unsafe { tree_sitter_elvish() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: [].into(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]"), ("|", "|")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/elvish.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Erlang => {
let language = unsafe { tree_sitter_erlang() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: [].into(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("[", "]")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/erlang.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
FSharp => {
let language = unsafe { tree_sitter_fsharp() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string", "triple_quoted_string"].into(),
delimiter_tokens: vec![("(", ")"), ("[", "]"), ("{", "}")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/f-sharp.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Gleam => {
let language = unsafe { tree_sitter_gleam() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string"].into(),
delimiter_tokens: vec![("(", ")"), ("[", "]"), ("{", "}")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/gleam.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Go => {
let language_fn = tree_sitter_go::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["interpreted_string_literal", "raw_string_literal"]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]"), ("(", ")")]
.into_iter()
.collect(),
highlight_query: ts::Query::new(&language, tree_sitter_go::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Hack => {
let language = unsafe { tree_sitter_hack() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["prefixed_string", "heredoc"].into_iter().collect(),
delimiter_tokens: vec![("[", "]"), ("(", ")"), ("<", ">"), ("{", "}")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/hack.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Hare => {
let language = unsafe { tree_sitter_hare() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string_constant", "rune_constant"]
.into_iter()
.collect(),
delimiter_tokens: vec![("[", "]"), ("(", ")"), ("{", "}")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/hare.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Haskell => {
let language_fn = tree_sitter_haskell::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![
"qualified_variable",
"qualified_module",
"qualified_constructor",
"strict_type",
]
.into_iter()
.collect(),
delimiter_tokens: vec![("[", "]"), ("(", ")")],
highlight_query: ts::Query::new(&language, tree_sitter_haskell::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Hcl => {
let language = unsafe { tree_sitter_hcl() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string_lit", "heredoc_template"].into_iter().collect(),
delimiter_tokens: vec![
("[", "]"),
("(", ")"),
("{", "}"),
("%{", "}"),
("%{~", "~}"),
("${", "}"),
],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/hcl.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Html => {
let language_fn = tree_sitter_html::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![
"doctype",
"quoted_attribute_value",
"raw_text",
"tag_name",
"text",
]
.into_iter()
.collect(),
delimiter_tokens: vec![("<", ">"), ("<!", ">"), ("<!--", "-->")]
.into_iter()
.collect(),
highlight_query: ts::Query::new(&language, tree_sitter_html::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![
TreeSitterSubLanguage {
query: ts::Query::new(&language, "(style_element (raw_text) @contents)")
.unwrap(),
parse_as: Css,
},
TreeSitterSubLanguage {
query: ts::Query::new(&language, "(script_element (raw_text) @contents)")
.unwrap(),
parse_as: JavaScript,
},
],
}
}
Janet => {
let language = unsafe { tree_sitter_janet_simple() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![].into_iter().collect(),
delimiter_tokens: vec![
("@{", "}"),
("@(", ")"),
("@[", "]"),
("{", "}"),
("(", ")"),
("[", "]"),
]
.into_iter()
.collect(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/janet_simple.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Java => {
let language_fn = tree_sitter_java::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![
"string_literal",
"boolean_type",
"integral_type",
"floating_point_type",
"void_type",
]
.into_iter()
.collect(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("[", "]")],
highlight_query: ts::Query::new(&language, tree_sitter_java::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
JavaScript | JavascriptJsx => {
let language_fn = tree_sitter_javascript::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "template_string", "regex"]
.into_iter()
.collect(),
delimiter_tokens: vec![
("[", "]"),
("(", ")"),
("{", "}"),
("<", ">"),
],
highlight_query: ts::Query::new(&language, tree_sitter_javascript::HIGHLIGHT_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Json => {
let language_fn = tree_sitter_json::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]")],
highlight_query: ts::Query::new(&language, tree_sitter_json::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Julia => {
let language_fn = tree_sitter_julia::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![
"string_literal",
"prefixed_string_literal",
"command_literal",
"character_literal",
]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]"), ("(", ")")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/julia.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Kotlin => {
let language = unsafe { tree_sitter_kotlin() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["line_string_literal", "character_literal", "nullable_type"]
.into_iter()
.collect(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("[", "]"), ("<", ">")]
.into_iter()
.collect(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/kotlin.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
LaTeX => {
let language = unsafe { tree_sitter_latex() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/latex.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Lua => {
let language_fn = tree_sitter_lua::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string"].into_iter().collect(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("[", "]")]
.into_iter()
.collect(),
highlight_query: ts::Query::new(&language, tree_sitter_lua::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Make => {
let language_fn = tree_sitter_make::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["shell_text", "text"].into_iter().collect(),
delimiter_tokens: vec![("(", ")")].into_iter().collect(),
highlight_query: ts::Query::new(&language, tree_sitter_make::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![TreeSitterSubLanguage {
query: ts::Query::new(&language, "(shell_function (shell_command) @contents)")
.unwrap(),
parse_as: Bash,
}],
}
}
Newick => {
let language = unsafe { tree_sitter_newick() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![].into_iter().collect(),
delimiter_tokens: vec![("(", ")")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/newick.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Nix => {
let language = unsafe { tree_sitter_nix() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string_expression", "indented_string_expression"]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]")].into_iter().collect(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/nix.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
ObjC => {
let language_fn = tree_sitter_objc::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string_literal"].into_iter().collect(),
delimiter_tokens: vec![
("(", ")"),
("{", "}"),
("[", "]"),
("@(", ")"),
("@{", "}"),
("@[", "]"),
],
highlight_query: ts::Query::new(&language, tree_sitter_objc::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
OCaml => {
let language_fn = tree_sitter_ocaml::LANGUAGE_OCAML;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: OCAML_ATOM_NODES.iter().copied().collect(),
delimiter_tokens: vec![("(", ")"), ("[", "]"), ("{", "}")],
highlight_query: ts::Query::new(&language, tree_sitter_ocaml::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
OCamlInterface => {
let language_fn = tree_sitter_ocaml::LANGUAGE_OCAML_INTERFACE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: OCAML_ATOM_NODES.iter().copied().collect(),
delimiter_tokens: vec![("(", ")"), ("[", "]"), ("{", "}")],
highlight_query: ts::Query::new(&language, "").unwrap(),
sub_languages: vec![],
}
}
Pascal => {
let language = unsafe { tree_sitter_pascal() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![].into_iter().collect(),
delimiter_tokens: vec![("(", ")"), ("[", "]")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/pascal.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Perl => {
let language = unsafe { tree_sitter_perl() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![
"string_single_quoted",
"string_double_quoted",
"comments",
"command_qx_quoted",
"patter_matcher_m",
"regex_pattern_qr",
"transliteration_tr_or_y",
"substitution_pattern_s",
]
.into_iter()
.collect(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("[", "]")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/perl.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Php => {
let language_fn = tree_sitter_php::LANGUAGE_PHP;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "encapsed_string"].into_iter().collect(),
delimiter_tokens: vec![("(", ")"), ("[", "]"), ("{", "}")],
highlight_query: ts::Query::new(&language, tree_sitter_php::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Python => {
let language_fn = tree_sitter_python::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string"].into_iter().collect(),
delimiter_tokens: vec![("(", ")"), ("[", "]"), ("{", "}")],
highlight_query: ts::Query::new(&language, tree_sitter_python::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Qml => {
let language = unsafe { tree_sitter_qmljs() };
let mut highlight_query = tree_sitter_javascript::HIGHLIGHT_QUERY.to_owned();
highlight_query.push_str(tree_sitter_typescript::HIGHLIGHTS_QUERY);
highlight_query.push_str(include_str!("../../vendored_parsers/highlights/qmljs.scm"));
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "template_string", "regex"]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]"), ("<", ">")],
highlight_query: ts::Query::new(&language, &highlight_query).unwrap(),
sub_languages: vec![],
}
}
R => {
let language = unsafe { tree_sitter_r() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "special"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/r.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Racket => {
let language = unsafe { tree_sitter_racket() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "byte_string", "regex", "here_string"]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/racket.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Ruby => {
let language_fn = tree_sitter_ruby::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "heredoc_body", "regex"]
.into_iter()
.collect(),
delimiter_tokens: vec![
("{", "}"),
("(", ")"),
("[", "]"),
("|", "|"),
("def", "end"),
("begin", "end"),
("class", "end"),
],
highlight_query: ts::Query::new(&language, tree_sitter_ruby::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Rust => {
let language = unsafe { tree_sitter_rust() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["char_literal", "string_literal"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]"), ("|", "|"), ("<", ">")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/rust.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Scala => {
let language_fn = tree_sitter_scala::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "template_string"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]")],
highlight_query: ts::Query::new(&language, tree_sitter_scala::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Scheme => {
let language = unsafe { tree_sitter_scheme() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/scheme.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Scss => {
let language = unsafe { tree_sitter_scss() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["integer_value", "float_value", "color_value"]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/scss.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Smali => {
let language = unsafe { tree_sitter_smali() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: HashSet::from(["string"]),
delimiter_tokens: Vec::new(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/smali.scm"),
)
.unwrap(),
sub_languages: Vec::new(),
}
}
Solidity => {
let language = unsafe { tree_sitter_solidity() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "hex_string_literal", "unicode_string_literal"]
.into_iter()
.collect(),
delimiter_tokens: vec![("[", "]"), ("(", ")"), ("{", "}")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/solidity.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Sql => {
let language = unsafe { tree_sitter_sql() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "identifier"].into_iter().collect(),
delimiter_tokens: vec![("(", ")")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/sql.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Swift => {
let language = unsafe { tree_sitter_swift() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["line_string_literal"].into(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]"), ("<", ">")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/swift.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Toml => {
let language_fn = tree_sitter_toml_ng::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "quoted_key"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]")],
highlight_query: ts::Query::new(&language, tree_sitter_toml_ng::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
TypeScriptTsx => {
let language_fn = tree_sitter_typescript::LANGUAGE_TSX;
let language = tree_sitter::Language::new(language_fn);
let mut highlight_query = tree_sitter_javascript::HIGHLIGHT_QUERY.to_owned();
highlight_query.push_str(tree_sitter_typescript::HIGHLIGHTS_QUERY);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "template_string"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]"), ("<", ">")],
highlight_query: ts::Query::new(&language, &highlight_query).unwrap(),
sub_languages: vec![],
}
}
TypeScript => {
let language_fn = tree_sitter_typescript::LANGUAGE_TYPESCRIPT;
let language = tree_sitter::Language::new(language_fn);
let mut highlight_query = tree_sitter_javascript::HIGHLIGHT_QUERY.to_owned();
highlight_query.push_str(tree_sitter_typescript::HIGHLIGHTS_QUERY);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["string", "template_string", "regex", "predefined_type"]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]"), ("<", ">")],
highlight_query: ts::Query::new(&language, &highlight_query).unwrap(),
sub_languages: vec![],
}
}
Xml => {
let language_fn = tree_sitter_xml::LANGUAGE_XML;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["AttValue", "XMLDecl"].into_iter().collect(),
delimiter_tokens: (vec![("<", ">")]),
highlight_query: ts::Query::new(&language, tree_sitter_xml::XML_HIGHLIGHT_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Yaml => {
let language_fn = tree_sitter_yaml::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![
"string_scalar",
"double_quote_scalar",
"single_quote_scalar",
"block_scalar",
]
.into_iter()
.collect(),
delimiter_tokens: (vec![("{", "}"), ("(", ")"), ("[", "]")]),
highlight_query: ts::Query::new(&language, tree_sitter_yaml::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
Vhdl => {
let language = unsafe { tree_sitter_vhdl() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec![].into_iter().collect(),
delimiter_tokens: vec![("(", ")")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/vhdl.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Zig => {
let language = unsafe { tree_sitter_zig() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: vec!["STRINGLITERALSINGLE", "BUILTINIDENTIFIER"]
.into_iter()
.collect(),
delimiter_tokens: (vec![("{", "}"), ("[", "]"), ("(", ")")])
.into_iter()
.collect(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/zig.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
}
}
pub(crate) fn to_tree(src: &str, config: &TreeSitterConfig) -> tree_sitter::Tree {
let mut parser = ts::Parser::new();
parser
.set_language(&config.language)
.expect("Incompatible tree-sitter version");
parser.parse(src, None).unwrap()
}
#[derive(Debug)]
pub(crate) struct ExceededByteLimit(pub(crate) usize);
pub(crate) fn to_tree_with_limit(
diff_options: &DiffOptions,
config: &TreeSitterConfig,
lhs_src: &str,
rhs_src: &str,
) -> Result<(tree_sitter::Tree, tree_sitter::Tree), ExceededByteLimit> {
if lhs_src.len() > diff_options.byte_limit || rhs_src.len() > diff_options.byte_limit {
let num_bytes = std::cmp::max(lhs_src.len(), rhs_src.len());
return Err(ExceededByteLimit(num_bytes));
}
Ok((to_tree(lhs_src, config), to_tree(rhs_src, config)))
}
pub(crate) fn parse_subtrees(
src: &str,
config: &TreeSitterConfig,
tree: &tree_sitter::Tree,
) -> DftHashMap<usize, (tree_sitter::Tree, TreeSitterConfig, HighlightedNodeIds)> {
let mut subtrees = DftHashMap::default();
for language in &config.sub_languages {
let mut query_cursor = tree_sitter::QueryCursor::new();
let mut query_matches =
query_cursor.matches(&language.query, tree.root_node(), src.as_bytes());
while let Some(m) = query_matches.next() {
let node = m.nodes_for_capture_index(0).next().unwrap();
if node.byte_range().is_empty() {
continue;
}
let subconfig = from_language(language.parse_as);
let mut parser = ts::Parser::new();
parser
.set_language(&subconfig.language)
.expect("Incompatible tree-sitter version");
parser
.set_included_ranges(&[node.range()])
.expect("Incompatible tree-sitter version");
let tree = parser.parse(src, None).unwrap();
let sub_highlights = tree_highlights(&tree, src, &subconfig);
subtrees.insert(node.id(), (tree, subconfig, sub_highlights));
}
}
subtrees
}
fn tree_highlights(
tree: &tree_sitter::Tree,
src: &str,
config: &TreeSitterConfig,
) -> HighlightedNodeIds {
let mut keyword_ish_capture_ids: Vec<u32> = vec![];
let mut string_capture_ids = vec![];
let mut type_capture_ids = vec![];
let mut comment_capture_ids = vec![];
let cn = config.highlight_query.capture_names();
for (idx, name) in cn.iter().enumerate() {
let name = *name;
if name == "type"
|| name.starts_with("type.")
|| name.starts_with("storage.type.")
|| name.starts_with("keyword.type.")
|| name == "tag"
|| name == "constructor"
{
type_capture_ids.push(idx as u32);
} else if name == "keyword"
|| name.starts_with("keyword.")
|| name == "constant"
|| name.starts_with("constant.")
|| name == "operator"
|| name == "repeat"
|| name == "conditional"
|| name == "boolean"
|| name == "exception"
|| name == "include"
{
keyword_ish_capture_ids.push(idx as u32);
}
if name == "string"
|| name.starts_with("string.")
|| name == "character"
|| name.starts_with("character.")
{
string_capture_ids.push(idx as u32);
}
if name == "label" {
type_capture_ids.push(idx as u32);
}
if name == "comment" || name.starts_with("comment.") {
comment_capture_ids.push(idx as u32);
}
}
let mut qc = ts::QueryCursor::new();
let mut q_matches = qc.matches(&config.highlight_query, tree.root_node(), src.as_bytes());
let mut comment_ids = HashSet::new();
let mut keyword_ids = HashSet::new();
let mut string_ids = HashSet::new();
let mut type_ids = HashSet::new();
while let Some(m) = q_matches.next() {
for c in m.captures {
if comment_capture_ids.contains(&c.index) {
comment_ids.insert(c.node.id());
} else if keyword_ish_capture_ids.contains(&c.index) {
keyword_ids.insert(c.node.id());
} else if string_capture_ids.contains(&c.index) {
string_ids.insert(c.node.id());
} else if type_capture_ids.contains(&c.index) {
type_ids.insert(c.node.id());
}
}
}
HighlightedNodeIds {
comment_ids,
keyword_ids,
string_ids,
type_ids,
}
}
pub(crate) fn print_tree(src: &str, tree: &tree_sitter::Tree) {
let mut cursor = tree.walk();
print_cursor(src, &mut cursor, 0);
}
fn print_cursor(src: &str, cursor: &mut ts::TreeCursor, depth: usize) {
loop {
let node = cursor.node();
let formatted_node = format!(
"{} {} - {}",
node.kind().replace('\n', "\\n"),
node.start_position(),
node.end_position()
);
if node.child_count() == 0 {
let node_src = &src[node.start_byte()..node.end_byte()];
println!("{}{} {:?}", " ".repeat(depth), formatted_node, node_src);
} else {
println!("{}{}", " ".repeat(depth), formatted_node,);
}
if cursor.goto_first_child() {
print_cursor(src, cursor, depth + 1);
cursor.goto_parent();
}
if !cursor.goto_next_sibling() {
break;
}
}
}
pub(crate) fn comment_positions(
tree: &tree_sitter::Tree,
src: &str,
config: &TreeSitterConfig,
) -> Vec<MatchedPos> {
let arena = Arena::new();
let ignore_comments = false;
let (nodes, _err_count) = to_syntax(tree, src, &arena, config, ignore_comments);
let positions = syntax::comment_positions(&nodes);
positions
.into_iter()
.map(|pos| MatchedPos {
kind: syntax::MatchKind::Ignored {
highlight: syntax::TokenKind::Atom(AtomKind::Comment),
},
pos,
})
.collect()
}
#[derive(Debug)]
pub(crate) struct ExceededParseErrorLimit(pub(crate) usize);
pub(crate) fn to_syntax_with_limit<'a>(
lhs_src: &str,
rhs_src: &str,
lhs_tree: &tree_sitter::Tree,
rhs_tree: &tree_sitter::Tree,
arena: &'a Arena<Syntax<'a>>,
config: &TreeSitterConfig,
diff_options: &DiffOptions,
) -> Result<(Vec<&'a Syntax<'a>>, Vec<&'a Syntax<'a>>), ExceededParseErrorLimit> {
let (lhs_nodes, lhs_error_count) = to_syntax(
lhs_tree,
lhs_src,
arena,
config,
diff_options.ignore_comments,
);
let (rhs_nodes, rhs_error_count) = to_syntax(
rhs_tree,
rhs_src,
arena,
config,
diff_options.ignore_comments,
);
syntax::init_all_info(&lhs_nodes, &rhs_nodes);
let error_count = lhs_error_count + rhs_error_count;
if error_count > diff_options.parse_error_limit {
return Err(ExceededParseErrorLimit(error_count));
}
Ok((lhs_nodes, rhs_nodes))
}
pub(crate) fn to_syntax<'a>(
tree: &tree_sitter::Tree,
src: &str,
arena: &'a Arena<Syntax<'a>>,
config: &TreeSitterConfig,
ignore_comments: bool,
) -> (Vec<&'a Syntax<'a>>, usize) {
if src.trim().is_empty() {
return (vec![], 0);
}
let highlights = tree_highlights(tree, src, config);
let subtrees = parse_subtrees(src, config, tree);
let nl_pos = LinePositions::from(src);
let mut cursor = tree.walk();
let mut error_count: usize = 0;
if cursor.node().is_error() {
error_count += 1;
}
cursor.goto_first_child();
let nodes = all_syntaxes_from_cursor(
arena,
src,
&nl_pos,
&mut cursor,
&mut error_count,
config,
&highlights,
&subtrees,
ignore_comments,
);
(nodes, error_count)
}
pub(crate) fn parse<'a>(
arena: &'a Arena<Syntax<'a>>,
src: &str,
config: &TreeSitterConfig,
ignore_comments: bool,
) -> Vec<&'a Syntax<'a>> {
let tree = to_tree(src, config);
let (nodes, _err_count) = to_syntax(&tree, src, arena, config, ignore_comments);
nodes
}
fn child_tokens<'a>(src: &'a str, cursor: &mut ts::TreeCursor) -> Vec<Option<&'a str>> {
let mut tokens = vec![];
cursor.goto_first_child();
loop {
let node = cursor.node();
if node.child_count() > 1 || node.is_extra() {
tokens.push(None);
} else {
tokens.push(Some(&src[node.start_byte()..node.end_byte()]));
}
if !cursor.goto_next_sibling() {
break;
}
}
cursor.goto_parent();
tokens
}
fn find_delim_positions(
src: &str,
cursor: &mut ts::TreeCursor,
lang_delims: &[(&str, &str)],
) -> Option<(usize, usize)> {
let tokens = child_tokens(src, cursor);
for (i, token) in tokens.iter().enumerate() {
for (open_delim, close_delim) in lang_delims {
if *token == Some(open_delim) {
for (j, token) in tokens.iter().skip(i + 1).enumerate() {
if *token == Some(close_delim) {
return Some((i, i + 1 + j));
}
}
}
}
}
None
}
#[derive(Debug)]
pub(crate) struct HighlightedNodeIds {
keyword_ids: HashSet<usize>,
comment_ids: HashSet<usize>,
string_ids: HashSet<usize>,
type_ids: HashSet<usize>,
}
fn all_syntaxes_from_cursor<'a>(
arena: &'a Arena<Syntax<'a>>,
src: &str,
nl_pos: &LinePositions,
cursor: &mut ts::TreeCursor,
error_count: &mut usize,
config: &TreeSitterConfig,
highlights: &HighlightedNodeIds,
subtrees: &DftHashMap<usize, (tree_sitter::Tree, TreeSitterConfig, HighlightedNodeIds)>,
ignore_comments: bool,
) -> Vec<&'a Syntax<'a>> {
let mut nodes: Vec<&Syntax> = vec![];
loop {
nodes.extend(syntax_from_cursor(
arena,
src,
nl_pos,
cursor,
error_count,
config,
highlights,
subtrees,
ignore_comments,
));
if !cursor.goto_next_sibling() {
break;
}
}
nodes
}
fn syntax_from_cursor<'a>(
arena: &'a Arena<Syntax<'a>>,
src: &str,
nl_pos: &LinePositions,
cursor: &mut ts::TreeCursor,
error_count: &mut usize,
config: &TreeSitterConfig,
highlights: &HighlightedNodeIds,
subtrees: &DftHashMap<usize, (tree_sitter::Tree, TreeSitterConfig, HighlightedNodeIds)>,
ignore_comments: bool,
) -> Option<&'a Syntax<'a>> {
let node = cursor.node();
if let Some((subtree, subconfig, subhighlights)) = subtrees.get(&node.id()) {
let mut sub_cursor = subtree.walk();
return syntax_from_cursor(
arena,
src,
nl_pos,
&mut sub_cursor,
error_count,
subconfig,
subhighlights,
&DftHashMap::default(),
ignore_comments,
);
}
if node.is_error() {
*error_count += 1;
}
if config.atom_nodes.contains(node.kind()) || highlights.comment_ids.contains(&node.id()) {
atom_from_cursor(arena, src, nl_pos, cursor, highlights, ignore_comments)
} else if highlights.keyword_ids.contains(&node.id()) && node.child_count() == 1 {
atom_from_cursor(arena, src, nl_pos, cursor, highlights, ignore_comments)
} else if node.child_count() > 0 {
Some(list_from_cursor(
arena,
src,
nl_pos,
cursor,
error_count,
config,
highlights,
subtrees,
ignore_comments,
))
} else {
atom_from_cursor(arena, src, nl_pos, cursor, highlights, ignore_comments)
}
}
fn list_from_cursor<'a>(
arena: &'a Arena<Syntax<'a>>,
src: &str,
nl_pos: &LinePositions,
cursor: &mut ts::TreeCursor,
error_count: &mut usize,
config: &TreeSitterConfig,
highlights: &HighlightedNodeIds,
subtrees: &DftHashMap<usize, (tree_sitter::Tree, TreeSitterConfig, HighlightedNodeIds)>,
ignore_comments: bool,
) -> &'a Syntax<'a> {
let root_node = cursor.node();
let outer_open_content = "";
let outer_open_position = nl_pos.from_region(root_node.start_byte(), root_node.start_byte());
let outer_close_content = "";
let outer_close_position = nl_pos.from_region(root_node.end_byte(), root_node.end_byte());
let (i, j) = match find_delim_positions(src, cursor, &config.delimiter_tokens) {
Some((i, j)) => (i as isize, j as isize),
None => (-1, root_node.child_count() as isize),
};
let mut inner_open_content = outer_open_content;
let mut inner_open_position = outer_open_position.clone();
let mut inner_close_content = outer_close_content;
let mut inner_close_position = outer_close_position.clone();
let mut before_delim = vec![];
let mut between_delim = vec![];
let mut after_delim = vec![];
let mut node_i = 0;
cursor.goto_first_child();
loop {
let node = cursor.node();
if node_i < i {
before_delim.extend(syntax_from_cursor(
arena,
src,
nl_pos,
cursor,
error_count,
config,
highlights,
subtrees,
ignore_comments,
));
} else if node_i == i {
inner_open_content = &src[node.start_byte()..node.end_byte()];
inner_open_position = nl_pos.from_region(node.start_byte(), node.end_byte());
} else if node_i < j {
between_delim.extend(syntax_from_cursor(
arena,
src,
nl_pos,
cursor,
error_count,
config,
highlights,
subtrees,
ignore_comments,
));
} else if node_i == j {
inner_close_content = &src[node.start_byte()..node.end_byte()];
inner_close_position = nl_pos.from_region(node.start_byte(), node.end_byte());
} else if node_i > j {
after_delim.extend(syntax_from_cursor(
arena,
src,
nl_pos,
cursor,
error_count,
config,
highlights,
subtrees,
ignore_comments,
));
}
if !cursor.goto_next_sibling() {
break;
}
node_i += 1;
}
cursor.goto_parent();
let inner_list = Syntax::new_list(
arena,
inner_open_content,
inner_open_position,
between_delim,
inner_close_content,
inner_close_position,
);
if before_delim.is_empty() && after_delim.is_empty() {
inner_list
} else {
let mut children = before_delim;
children.push(inner_list);
children.append(&mut after_delim);
Syntax::new_list(
arena,
outer_open_content,
outer_open_position,
children,
outer_close_content,
outer_close_position,
)
}
}
fn atom_from_cursor<'a>(
arena: &'a Arena<Syntax<'a>>,
src: &str,
nl_pos: &LinePositions,
cursor: &mut ts::TreeCursor,
highlights: &HighlightedNodeIds,
ignore_comments: bool,
) -> Option<&'a Syntax<'a>> {
let node = cursor.node();
let position = nl_pos.from_region(node.start_byte(), node.end_byte());
let mut content = &src[node.start_byte()..node.end_byte()];
if node.kind() == "\n" {
return None;
}
if node.kind() == "jsx_text" {
content = content.trim();
}
let highlight = if node.is_error() {
AtomKind::TreeSitterError
} else if node.is_extra()
|| node.kind() == "comment"
|| highlights.comment_ids.contains(&node.id())
{
if ignore_comments {
return None;
}
AtomKind::Comment
} else if highlights.keyword_ids.contains(&node.id()) {
AtomKind::Keyword
} else if highlights.string_ids.contains(&node.id()) {
AtomKind::String(StringKind::StringLiteral)
} else if highlights.type_ids.contains(&node.id()) {
AtomKind::Type
} else if node.kind() == "CharData" || node.kind() == "text" {
AtomKind::String(StringKind::Text)
} else {
AtomKind::Normal
};
Some(Syntax::new_atom(arena, position, content, highlight))
}
#[cfg(test)]
mod tests {
use strum::IntoEnumIterator as _;
use super::*;
#[test]
fn test_parse() {
let arena = Arena::new();
let css_config = from_language(guess::Language::Css);
parse(&arena, ".foo {}", &css_config, false);
}
#[test]
fn test_parse_empty_file() {
let arena = Arena::new();
let config = from_language(guess::Language::EmacsLisp);
let res = parse(&arena, "", &config, false);
let expected: Vec<&Syntax> = vec![];
assert_eq!(res, expected);
}
#[test]
fn test_subtrees() {
let arena = Arena::new();
let config = from_language(guess::Language::Html);
let res = parse(&arena, "<style>.a { color: red; }</style>", &config, false);
match res[0] {
Syntax::List { children, .. } => {
assert_eq!(children.len(), 3);
assert!(matches!(children[1], Syntax::List { .. }));
}
_ => {
panic!("Top level isn't a list");
}
};
}
#[test]
fn test_configs_valid() {
for language in guess::Language::iter() {
from_language(language);
}
}
}