Skip to content

Commit

Permalink
support Jinja/Twig (close #1)
Browse files Browse the repository at this point in the history
  • Loading branch information
g-plane committed Nov 3, 2023
1 parent d88a3a0 commit 79efec1
Show file tree
Hide file tree
Showing 23 changed files with 563 additions and 94 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# markup_fmt

markup_fmt is a configurable HTML/Vue/Svelte formatter.
markup_fmt is a configurable HTML/Vue/Svelte/Jinja/Twig formatter.

## Notes for Vue and Svelte Users

Expand All @@ -17,7 +17,7 @@ This will make ESLint faster because less rules will be executed.

We've provided [dprint](https://dprint.dev/) integration.

This plugin only formats HTML syntax of your HTML/Vue/Svelte files.
This plugin only formats HTML syntax of your HTML/Vue/Svelte/Jinja/Twig files.
You also need other dprint plugins to format the code in `<script>` and `<style>` tags.
You can use [dprint-plugin-typescript](https://github.com/dprint/dprint-plugin-typescript) to
format TypeScript/JavaScript code and [Malva](https://github.com/g-plane/malva) to format CSS/SCSS/Sass/Less code.
Expand Down
22 changes: 10 additions & 12 deletions dprint_plugin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use dprint_core::{
configuration::{ConfigKeyMap, GlobalConfiguration, ResolveConfigurationResult},
plugins::{FileMatchingInfo, PluginInfo, SyncPluginHandler, SyncPluginInfo},
};
use markup_fmt::{config::FormatOptions, format_text, FormatError, Language};
use markup_fmt::{config::FormatOptions, detect_language, format_text, FormatError};
use std::path::Path;

mod config;
Expand Down Expand Up @@ -34,7 +34,10 @@ impl SyncPluginHandler<FormatOptions> for MarkupFmtPluginHandler {
),
},
file_matching: FileMatchingInfo {
file_extensions: vec!["html".into(), "vue".into(), "svelte".into()],
file_extensions: ["html", "vue", "svelte", "jinja", "jinja2", "twig"]
.into_iter()
.map(String::from)
.collect(),
file_names: vec![],
},
}
Expand All @@ -59,16 +62,11 @@ impl SyncPluginHandler<FormatOptions> for MarkupFmtPluginHandler {
config: &FormatOptions,
mut format_with_host: impl FnMut(&Path, String, &ConfigKeyMap) -> Result<Option<String>>,
) -> Result<Option<String>> {
let language = match file_path.extension().and_then(|s| s.to_str()) {
Some(ext) if ext.eq_ignore_ascii_case("html") => Language::Html,
Some(ext) if ext.eq_ignore_ascii_case("vue") => Language::Vue,
Some(ext) if ext.eq_ignore_ascii_case("svelte") => Language::Svelte,
_ => {
return Err(anyhow::anyhow!(
"unknown file extension of file: {}",
file_path.display()
));
}
let Some(language) = detect_language(file_path) else {
return Err(anyhow::anyhow!(
"unknown file extension of file: {}",
file_path.display()
));
};

let format_result = format_text(file_text, language, config, |path, code, print_width| {
Expand Down
2 changes: 1 addition & 1 deletion markup_fmt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "markup_fmt"
version = "0.1.1"
edition = "2021"
authors = ["Pig Fang <g-plane@hotmail.com>"]
description = "Configurable HTML/Vue/Svelte formatter."
description = "Configurable HTML/Vue/Svelte/Jinja/Twig formatter."
repository = "https://github.com/g-plane/markup_fmt"
license = "MIT"
exclude = ["/tests"]
Expand Down
2 changes: 1 addition & 1 deletion markup_fmt/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
markup_fmt is a configurable HTML/Vue/Svelte formatter.
markup_fmt is a configurable HTML/Vue/Svelte/Jinja/Twig formatter.

## Basic Usage

Expand Down
30 changes: 30 additions & 0 deletions markup_fmt/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ pub struct Element<'s> {
pub void_element: bool,
}

#[derive(Clone, Debug)]
pub struct JinjaBlock<'s> {
pub body: Vec<JinjaTagOrChildren<'s>>,
}

#[derive(Clone, Debug)]
pub struct JinjaComment<'s> {
pub raw: &'s str,
}

#[derive(Clone, Debug)]
pub struct JinjaInterpolation<'s> {
pub expr: &'s str,
}

#[derive(Clone, Debug)]
pub struct JinjaTag<'s> {
pub content: &'s str,
}

#[derive(Clone, Debug)]
pub enum JinjaTagOrChildren<'s> {
Tag(JinjaTag<'s>),
Children(Vec<Node<'s>>),
}

#[derive(Clone, Debug)]
pub struct NativeAttribute<'s> {
pub name: &'s str,
Expand All @@ -38,6 +64,10 @@ pub enum Node<'s> {
Comment(Comment<'s>),
Doctype,
Element(Element<'s>),
JinjaBlock(JinjaBlock<'s>),
JinjaComment(JinjaComment<'s>),
JinjaInterpolation(JinjaInterpolation<'s>),
JinjaTag(JinjaTag<'s>),
SvelteAtTag(SvelteAtTag<'s>),
SvelteAwaitBlock(Box<SvelteAwaitBlock<'s>>),
SvelteEachBlock(SvelteEachBlock<'s>),
Expand Down
4 changes: 2 additions & 2 deletions markup_fmt/src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ where
{
pub(crate) fn script_indent(&self) -> bool {
match self.language {
Language::Html => self
Language::Html | Language::Jinja => self
.options
.html_script_indent
.unwrap_or(self.options.script_indent),
Expand All @@ -46,7 +46,7 @@ where

pub(crate) fn style_indent(&self) -> bool {
match self.language {
Language::Html => self
Language::Html | Language::Jinja => self
.options
.html_style_indent
.unwrap_or(self.options.style_indent),
Expand Down
10 changes: 8 additions & 2 deletions markup_fmt/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ pub enum SyntaxErrorKind {
ExpectDoctype,
ExpectElement,
ExpectIdentifier,
ExpectJinjaBlockEnd,
ExpectJinjaTag,
ExpectKeyword(&'static str),
ExpectMustacheInterpolation,
ExpectSelfCloseTag,
ExpectSvelteAtTag,
ExpectSvelteAttr,
Expand All @@ -33,7 +36,6 @@ pub enum SyntaxErrorKind {
ExpectTagName,
ExpectTextNode,
ExpectVueDirective,
ExpectVueInterpolation,
UnknownSvelteBlock,
}

Expand All @@ -48,9 +50,14 @@ impl fmt::Display for SyntaxError {
SyntaxErrorKind::ExpectDoctype => "expect HTML doctype".into(),
SyntaxErrorKind::ExpectElement => "expect element".into(),
SyntaxErrorKind::ExpectIdentifier => "expect identifier".into(),
SyntaxErrorKind::ExpectJinjaBlockEnd => "expect Jinja block end".into(),
SyntaxErrorKind::ExpectJinjaTag => "expect Jinja tag".into(),
SyntaxErrorKind::ExpectKeyword(keyword) => {
format!("expect keyword '{}'", keyword).into()
}
SyntaxErrorKind::ExpectMustacheInterpolation => {
"expect mustache-like interpolation".into()
}
SyntaxErrorKind::ExpectSelfCloseTag => "expect self close tag".into(),
SyntaxErrorKind::ExpectSvelteAtTag => "expect Svelte `{@` tag".into(),
SyntaxErrorKind::ExpectSvelteAttr => "expect Svelte attribute".into(),
Expand All @@ -66,7 +73,6 @@ impl fmt::Display for SyntaxError {
SyntaxErrorKind::ExpectTagName => "expect tag name".into(),
SyntaxErrorKind::ExpectTextNode => "expect text node".into(),
SyntaxErrorKind::ExpectVueDirective => "expect Vue directive".into(),
SyntaxErrorKind::ExpectVueInterpolation => "expect Vue interpolation".into(),
SyntaxErrorKind::UnknownSvelteBlock => "unknown Svelte block".into(),
};

Expand Down
10 changes: 5 additions & 5 deletions markup_fmt/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ static NON_WS_SENSITIVE_TAGS: [&str; 69] = [
];

pub(crate) fn is_whitespace_sensitive_tag(name: &str, language: Language) -> bool {
if matches!(language, Language::Html) {
if matches!(language, Language::Html | Language::Jinja) {
// There's also a tag called "a" in SVG, so we need to check it specially.
name.eq_ignore_ascii_case("a")
|| !NON_WS_SENSITIVE_TAGS
Expand All @@ -99,7 +99,7 @@ static VOID_ELEMENTS: [&str; 14] = [
];

pub(crate) fn is_void_element(name: &str, language: Language) -> bool {
if matches!(language, Language::Html) {
if matches!(language, Language::Html | Language::Jinja) {
VOID_ELEMENTS
.iter()
.any(|tag| tag.eq_ignore_ascii_case(name))
Expand All @@ -109,7 +109,7 @@ pub(crate) fn is_void_element(name: &str, language: Language) -> bool {
}

pub(crate) fn is_html_tag(name: &str, language: Language) -> bool {
if matches!(language, Language::Html) {
if matches!(language, Language::Html | Language::Jinja) {
css_dataset::tags::STANDARD_HTML_TAGS
.iter()
.any(|tag| tag.eq_ignore_ascii_case(name))
Expand All @@ -127,7 +127,7 @@ pub(crate) fn is_html_tag(name: &str, language: Language) -> bool {
}

pub(crate) fn is_svg_tag(name: &str, language: Language) -> bool {
if matches!(language, Language::Html) {
if matches!(language, Language::Html | Language::Jinja) {
css_dataset::tags::SVG_TAGS
.iter()
.any(|tag| tag.eq_ignore_ascii_case(name))
Expand All @@ -137,7 +137,7 @@ pub(crate) fn is_svg_tag(name: &str, language: Language) -> bool {
}

pub(crate) fn is_mathml_tag(name: &str, language: Language) -> bool {
if matches!(language, Language::Html) {
if matches!(language, Language::Html | Language::Jinja) {
css_dataset::tags::MATH_ML_TAGS
.iter()
.any(|tag| tag.eq_ignore_ascii_case(name))
Expand Down
11 changes: 11 additions & 0 deletions markup_fmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,14 @@ where
},
))
}

/// Detect language from file extension.
pub fn detect_language(path: impl AsRef<Path>) -> Option<Language> {
match path.as_ref().extension().and_then(|ext| ext.to_str()) {
Some("html") => Some(Language::Html),
Some("vue") => Some(Language::Vue),
Some("svelte") => Some(Language::Svelte),
Some("jinja" | "jinja2" | "twig") => Some(Language::Jinja),
_ => None,
}
}
14 changes: 3 additions & 11 deletions markup_fmt/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
use markup_fmt::{format_text, Language};
use std::{env, fs, path::Path};
use markup_fmt::{detect_language, format_text};
use std::{env, fs};

fn main() {
let file_path = env::args().nth(1).unwrap();
let language = match Path::new(&file_path)
.extension()
.and_then(|ext| ext.to_str())
{
Some("html") => Language::Html,
Some("vue") => Language::Vue,
Some("svelte") => Language::Svelte,
_ => panic!("Unsupported file extension"),
};
let language = detect_language(&file_path).unwrap();
let code = fs::read_to_string(file_path).unwrap();

let formatted = format_text(&code, language, &Default::default(), |_, code, _| {
Expand Down

0 comments on commit 79efec1

Please sign in to comment.