From 63380976586c628857ea0e43d2bb08eee57bc034 Mon Sep 17 00:00:00 2001 From: Artemy Egorov Date: Sun, 11 Aug 2024 16:26:34 +0300 Subject: [PATCH] feat: format cli command, fix format trim --- examples/daleth.dlth | 6 ++- src/commands.rs | 11 ++++-- src/daleth/format.rs | 82 ++++++++++++++++++++--------------------- src/daleth/lexer/mod.rs | 11 +++--- src/daleth/utils.rs | 24 ++++++++---- src/main.rs | 40 +++++++++++++++++++- 6 files changed, 114 insertions(+), 60 deletions(-) diff --git a/examples/daleth.dlth b/examples/daleth.dlth index d2a6ced..629cc5a 100644 --- a/examples/daleth.dlth +++ b/examples/daleth.dlth @@ -40,13 +40,15 @@ This is element br # if no tag is specified but a '{- text}' is present, then the 'p' tag is placed -# '\n' is deleted in this format. If a break line is needed in a paragraph, use ' \n'. +# '\n' is replaced with ' ' in this format. {- Check Dalet too This is one paragraph } -{- This is another paragraph ({- text\}) } +{- + This is another paragraph ({- text\}) +} row "center" [ link "https://github.com/txtdot/txtdot": Homepage diff --git a/src/commands.rs b/src/commands.rs index 5361b04..c99cf47 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,8 +1,10 @@ +use std::path::PathBuf; + use clap::{Parser, Subcommand}; #[derive(Debug, Parser)] -#[command(name = "cimengine", bin_name = "cimengine")] -#[command(about = "CIMEngine build tools")] +#[command(name = "dalet", bin_name = "dalet")] +#[command(about = "dalet cli")] pub struct Cli { #[command(subcommand)] pub cmd: Commands, @@ -10,4 +12,7 @@ pub struct Cli { #[derive(Debug, Subcommand)] #[clap(author, version, about)] -pub enum Commands {} +pub enum Commands { + /// Format file + Format { path: PathBuf }, +} diff --git a/src/daleth/format.rs b/src/daleth/format.rs index 0c242ce..6df0511 100644 --- a/src/daleth/format.rs +++ b/src/daleth/format.rs @@ -1,6 +1,6 @@ use super::{ lexer::types::{Spanned, Token}, - utils::set_indent, + utils::{prepend_indent, set_indent}, }; fn additional_str<'src>( @@ -83,72 +83,72 @@ pub fn format<'src>(spanned_tokens: &Vec>>) -> String { } Token::RSquare => { current_indent -= 1; - format!("{}\n", set_indent("]", current_indent)) + format!("{}\n", prepend_indent("]", current_indent)) } Token::NumberArgument(n) => format!("{n}"), Token::TextArgument(t) => format!(" \"{t}\""), - Token::TextBody(t) => format!(": {}\n", t.trim()), + Token::TextBody(t) => format!(": {}\n", t), Token::MLText(t) => format!( " {{\n{}\n{}\n", set_indent(t, current_indent + 1), - set_indent("}", current_indent) + prepend_indent("}", current_indent) ), Token::MLMSText(n, t) => format!( " {{~{n}\n{}\n{}\n", set_indent(t, current_indent + 1), - set_indent("}", current_indent) + prepend_indent("}", current_indent) ), Token::RMLText(t) => format!(" {{#{t}}}\n"), - Token::Comment(c) => format!("{}\n", set_indent(&format!("#{c}"), current_indent)), + Token::Comment(c) => format!("{}\n", prepend_indent(&format!("#{c}"), current_indent)), - Token::TextTag(t) => format!("{}\n", set_indent(t, current_indent)), + Token::TextTag(t) => format!("{}\n", prepend_indent(t, current_indent)), - Token::El => set_indent("el", current_indent), - Token::H => set_indent("h", current_indent), - Token::P => set_indent("p", current_indent), - Token::Br => set_indent("br", current_indent), - Token::Ul => set_indent("ul", current_indent), - Token::Ol => set_indent("ol", current_indent), - Token::Row => set_indent("row", current_indent), - Token::Link => set_indent("link", current_indent), - Token::Navlink => set_indent("navlink", current_indent), - Token::Btn => set_indent("btn", current_indent), - Token::Navbtn => set_indent("navbtn", current_indent), - Token::Img => set_indent("img", current_indent), - Token::Table => set_indent("table", current_indent), - Token::Tcol => set_indent("tcol", current_indent), - Token::Tpcol => set_indent("tpcol", current_indent), - Token::Hr => set_indent("hr", current_indent), - Token::B => set_indent("b", current_indent), - Token::I => set_indent("i", current_indent), - Token::Bq => set_indent("bq", current_indent), - Token::Footlnk => set_indent("footlnk", current_indent), - Token::Footn => set_indent("footn", current_indent), - Token::A => set_indent("a", current_indent), - Token::S => set_indent("s", current_indent), - Token::Sup => set_indent("sup", current_indent), - Token::Sub => set_indent("sub", current_indent), - Token::Disc => set_indent("disc", current_indent), - Token::Block => set_indent("block", current_indent), - Token::Carousel => set_indent("carousel", current_indent), - Token::Code => set_indent("code", current_indent), - Token::Pre => set_indent("pre", current_indent), - Token::Meta => set_indent("meta", current_indent), + Token::El => prepend_indent("el", current_indent), + Token::H => prepend_indent("h", current_indent), + Token::P => prepend_indent("p", current_indent), + Token::Br => prepend_indent("br", current_indent), + Token::Ul => prepend_indent("ul", current_indent), + Token::Ol => prepend_indent("ol", current_indent), + Token::Row => prepend_indent("row", current_indent), + Token::Link => prepend_indent("link", current_indent), + Token::Navlink => prepend_indent("navlink", current_indent), + Token::Btn => prepend_indent("btn", current_indent), + Token::Navbtn => prepend_indent("navbtn", current_indent), + Token::Img => prepend_indent("img", current_indent), + Token::Table => prepend_indent("table", current_indent), + Token::Tcol => prepend_indent("tcol", current_indent), + Token::Tpcol => prepend_indent("tpcol", current_indent), + Token::Hr => prepend_indent("hr", current_indent), + Token::B => prepend_indent("b", current_indent), + Token::I => prepend_indent("i", current_indent), + Token::Bq => prepend_indent("bq", current_indent), + Token::Footlnk => prepend_indent("footlnk", current_indent), + Token::Footn => prepend_indent("footn", current_indent), + Token::A => prepend_indent("a", current_indent), + Token::S => prepend_indent("s", current_indent), + Token::Sup => prepend_indent("sup", current_indent), + Token::Sub => prepend_indent("sub", current_indent), + Token::Disc => prepend_indent("disc", current_indent), + Token::Block => prepend_indent("block", current_indent), + Token::Carousel => prepend_indent("carousel", current_indent), + Token::Code => prepend_indent("code", current_indent), + Token::Pre => prepend_indent("pre", current_indent), + Token::Meta => prepend_indent("meta", current_indent), Token::ElOpen => { - let s = set_indent("[[", current_indent); + let s = prepend_indent("[[", current_indent); current_indent += 1; format!("{s}\n") } Token::ElClose => { current_indent -= 1; - format!("{}\n", set_indent("]]", current_indent)) + format!("{}\n", prepend_indent("]]", current_indent)) } Token::Paragraph(t) => format!( "{{-\n{}\n{}\n", set_indent(t, current_indent + 1), - set_indent("}", current_indent) + prepend_indent("}", current_indent) ), Token::EmptyLine => "\n".to_owned(), diff --git a/src/daleth/lexer/mod.rs b/src/daleth/lexer/mod.rs index d0d3262..9df1d08 100644 --- a/src/daleth/lexer/mod.rs +++ b/src/daleth/lexer/mod.rs @@ -75,7 +75,10 @@ pub fn lexer<'src>( .ignore_then(just('}')) .labelled("Multi-line escape sequence"); - let text = none_of("\n").repeated().to_slice(); + let text = none_of("\n") + .repeated() + .to_slice() + .padded_by(text::inline_whitespace()); let text_body = just(':') .ignore_then(text) @@ -90,18 +93,17 @@ pub fn lexer<'src>( let multiline_text_body = none_of("}\\") .or(escape) .repeated() + .to_slice() .labelled("Body of multiline text"); let paragraph = multiline_text_body .clone() - .to_slice() .delimited_by(just("{-"), just("}")) .map(Token::Paragraph) .labelled("Paragraph syntax"); let mltext = multiline_text_body .clone() - .to_slice() .delimited_by(just('{'), just('}')) .map(Token::MLText) .labelled("Multiline text"); @@ -112,14 +114,13 @@ pub fn lexer<'src>( .labelled("Minimum spaces number"); mlms_n - .then(multiline_text_body.clone().to_slice()) + .then(multiline_text_body.clone()) .then_ignore(just("}")) .map(|(n, t)| Token::MLMSText(n, t)) .labelled("Multi line text with min spaces") }; let rmltext = multiline_text_body - .to_slice() .delimited_by(just("{#"), just('}')) .map(Token::RMLText) .labelled("Raw multiline text"); diff --git a/src/daleth/utils.rs b/src/daleth/utils.rs index 365791b..c3956b1 100644 --- a/src/daleth/utils.rs +++ b/src/daleth/utils.rs @@ -1,5 +1,5 @@ pub fn trim_indent(input: &str) -> String { - let lines: Vec<&str> = input.lines().collect(); + let lines: Vec<&str> = trim_unused(input).lines().collect(); // Find the minimum indentation of non-empty lines let min_indent = lines @@ -21,28 +21,36 @@ pub fn trim_indent(input: &str) -> String { }) .collect(); - trim_newline(&trimmed_lines.join("\n")).to_owned() + trimmed_lines.join("\n") } pub fn set_indent(input: &str, indent: usize) -> String { - prepend_indent(&trim_indent(input), &" ".repeat(indent)) + prepend_indent(&trim_indent(input), indent) } -fn trim_newline<'a>(s: &'a str) -> &'a str { +fn trim_unused<'a>(s: &'a str) -> &'a str { let mut trim_start = 0; + let mut been_newlines = false; for start_char in s.chars() { - if start_char != '\n' && start_char != '\r' { + if !been_newlines + && (char::is_whitespace(start_char) && start_char != '\n' && start_char != '\r') + { + trim_start += 1; + continue; + } else if start_char != '\n' && start_char != '\r' { break; + } else { + been_newlines = true; + trim_start += 1; } - - trim_start += 1; } &s[(trim_start)..].trim_end() } -fn prepend_indent(input: &str, indent: &str) -> String { +pub fn prepend_indent(input: &str, indent: usize) -> String { + let indent = &" ".repeat(indent); let lines: Vec = input .lines() .map(|line| format!("{}{}", indent, line)) diff --git a/src/main.rs b/src/main.rs index 7de06b0..33268fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,41 @@ mod commands; -fn main() {} +use ariadne::{Color, Label, Report, ReportKind, Source}; +use chumsky::Parser; +use clap::Parser as ClapParser; +use commands::{Cli, Commands::*}; +use dalet::daleth::{format::format, lexer::lexer}; +use std::fs; + +fn main() { + let args = Cli::parse(); + + match args.cmd { + // TODO: add parser check before format + Format { path } => { + let src_file = &path.to_string_lossy().to_string(); + let src = fs::read_to_string(src_file).unwrap(); + + let parsed = lexer().parse(&src); + + match parsed.into_result() { + Ok(t) => { + fs::write(path, format(&t)).unwrap(); + } + Err(e) => e.into_iter().for_each(|e| { + Report::build(ReportKind::Error, src_file, e.span().start) + .with_code("Compiler") + .with_message(e.to_string().clone()) + .with_label( + Label::new((src_file, e.span().into_range())) + .with_message(e.to_string()) + .with_color(Color::Red), + ) + .finish() + .print((src_file, Source::from(&src))) + .unwrap() + }), + }; + } + } +}