Skip to content

Commit

Permalink
Finish implementing comment style inference
Browse files Browse the repository at this point in the history
  • Loading branch information
elkowar committed Dec 1, 2024
1 parent 6788d6d commit 04358b3
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 33 deletions.
25 changes: 3 additions & 22 deletions src/templating/document.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::eval_ctx::EvalCtx;
use crate::templating::parser::linewise::ParsedLine;
use crate::templating::COMMENT_START;

use super::{
element,
Expand All @@ -10,7 +9,6 @@ use super::{

use anyhow::Result;
use pest::Parser as _;
use regex::Regex;

#[derive(Debug)]
pub struct Document<'a> {
Expand Down Expand Up @@ -89,27 +87,10 @@ impl RenderContext {
lines.join("\n")
}
pub fn disabled_str(&self, s: &str) -> String {
let re = Regex::new(&format!("^\\s*{}{}", self.comment_style, COMMENT_START)).unwrap();
let lines: Vec<_> = s
let lines = s
.split('\n')
.map(|line| {
if !re.is_match(line) && !line.is_empty() {
let indent: String = line
.chars()
.take_while(|&c| c == ' ' || c == '\t')
.collect();
format!(
"{}{}{}{}",
indent,
self.comment_style,
COMMENT_START,
line.trim_start_matches(|c| c == ' ' || c == '\t')
)
} else {
line.to_string()
}
})
.collect();
.map(|x| self.comment_style.disable_line(x))
.collect::<Vec<_>>();
lines.join("\n")
}
}
161 changes: 150 additions & 11 deletions src/templating/parser/comment_style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,69 @@ pub enum CommentStyle {
}

impl CommentStyle {
pub fn enable_line<'a>(&self, line: &'a str) -> Cow<'a, str> {
#[allow(unused)]
pub fn prefix(left: &str) -> Self {
CommentStyle::Prefix(left.to_string())
}
#[allow(unused)]
pub fn circumfix(left: &str, right: &str) -> Self {
CommentStyle::Circumfix(left.to_string(), right.to_string())
}
pub fn left(&self) -> &str {
match self {
CommentStyle::Prefix(prefix) => {
// TODO: Creating a regex every time here is horrible
let re = Regex::new(&format!("{prefix}{COMMENT_START}")).unwrap();
re.replace_all(line, "")
}
CommentStyle::Circumfix(left, right) => {
let re_left = Regex::new(&format!("{left}{COMMENT_START}")).unwrap();
let re_right = Regex::new(right).unwrap();
let result = re_right.replace_all(line, "");
Cow::Owned(re_left.replace_all(&result, "").to_string())
CommentStyle::Prefix(left) => left,
CommentStyle::Circumfix(left, _) => left,
}
}
pub fn enable_line<'a>(&self, line: &'a str) -> Cow<'a, str> {
// TODO: Creating a regex every time here is horrible
let left = self.left();
let re = Regex::new(&format!(
"{}{}",
regex::escape(left),
regex::escape(COMMENT_START)
))
.unwrap();
let left_done = re.replace_all(line, "");
if let CommentStyle::Circumfix(_, right) = self {
let re_right = Regex::new(&regex::escape(right)).unwrap();
Cow::Owned(re_right.replace_all(&left_done, "").to_string())
} else {
left_done
}
}

pub fn is_disabled(&self, line: &str) -> bool {
let re = match self {
CommentStyle::Prefix(left) => {
format!("^.*{}{}", regex::escape(left), regex::escape(COMMENT_START))
}
CommentStyle::Circumfix(left, right) => format!(
"^.*{}{}.*{}",
regex::escape(left),
regex::escape(COMMENT_START),
regex::escape(right)
),
};
Regex::new(&re).unwrap().is_match(line)
}

pub fn disable_line<'a>(&self, line: &'a str) -> Cow<'a, str> {
if self.is_disabled(line) || line.trim().is_empty() {
return line.into();
}
let left = self.left();
let re = Regex::new(&format!("^(\\s*)(.*)$")).unwrap();
let (indent, remaining_line) = re
.captures(line)
.and_then(|x| (x.get(1).zip(x.get(2))))
.map(|(a, b)| (a.as_str(), b.as_str()))
.unwrap_or_default();
let right = match self {
CommentStyle::Prefix(_) => "".to_string(),
CommentStyle::Circumfix(_, right) => format!("{right}"),
};
format!("{indent}{left}{COMMENT_START}{remaining_line}{right}",).into()
}
}

Expand All @@ -56,3 +105,93 @@ pub fn infer_comment_syntax(line: &ParsedLine<'_>) -> Option<CommentStyle> {
}
None
}

#[cfg(test)]
mod test {
use pest::Span;

use crate::templating::{
parser::{
comment_style::infer_comment_syntax,
linewise::{ParsedLine, TagKind},
},
TaggedLine,
};

use super::CommentStyle;

#[track_caller]
fn assert_roundtrip_works(start: &str, expected_disabled: &str, comment_style: CommentStyle) {
let disabled = comment_style.disable_line(start);
let enabled = comment_style.enable_line(disabled.as_ref());
assert_eq!(expected_disabled, disabled);
assert_eq!(start, enabled);
}

#[test]
pub fn test_disable_enable_roundtrip() {
// This roundtrip (disable -> enable) should _always_ be identity
assert_roundtrip_works(" foo", " #<yolk> foo", CommentStyle::prefix("#"));

assert_roundtrip_works(
" foo",
" /*<yolk> foo*/",
CommentStyle::circumfix("/*", "*/"),
);
}

#[test]
pub fn test_enable_idempodent() {
let assert_idempotent = |comment_style: CommentStyle, line: &str| {
let enabled = comment_style.enable_line(&line);
let enabled_again = comment_style.enable_line(enabled.as_ref());
assert_eq!(enabled, enabled_again);
};
assert_idempotent(CommentStyle::prefix("#"), "\tfoo");
assert_idempotent(CommentStyle::prefix("#"), "foo ");
assert_idempotent(CommentStyle::circumfix("/*", "*/"), " foo ");
}

#[test]
pub fn test_disable_idempodent() {
let assert_idempotent = |comment_style: CommentStyle, line: &str| {
let disabled = comment_style.disable_line(line);
let disabled_again = comment_style.disable_line(disabled.as_ref());
assert_eq!(disabled, disabled_again);
};
assert_idempotent(CommentStyle::prefix("#"), "\tfoo");
assert_idempotent(CommentStyle::prefix("#"), "foo ");
assert_idempotent(CommentStyle::circumfix("/*", "*/"), " foo ");
}

#[test]
pub fn test_infer_comment_syntax() {
let parsed_line = ParsedLine::InlineTag {
line: TaggedLine {
full_line: Span::new("# {# foo #}", 0, 10).unwrap(),
tag: "{# foo #}",
left: "# ",
right: "",
},
kind: TagKind::Regular("foo"),
};
assert_eq!(
infer_comment_syntax(&parsed_line),
Some(CommentStyle::prefix("#"))
);

let parsed_line = ParsedLine::InlineTag {
line: TaggedLine {
full_line: Span::new("/* {# foo #} */", 0, 14).unwrap(),
tag: "{# foo #}",
left: "/* ",
right: " */",
},
kind: TagKind::Regular("foo"),
};
assert_eq!(
infer_comment_syntax(&parsed_line),
Some(CommentStyle::circumfix("/*", "*/"))
);
}
}

0 comments on commit 04358b3

Please sign in to comment.