Skip to content

Commit e566500

Browse files
committed
Markdown stuff
1 parent f56e4a8 commit e566500

File tree

9 files changed

+193
-28
lines changed

9 files changed

+193
-28
lines changed

internal/compiler/builtin_macros.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ pub fn lower_macro(
8787
}
8888
BuiltinMacroFunction::Rgb => rgb_macro(n, sub_expr.collect(), diag),
8989
BuiltinMacroFunction::Hsv => hsv_macro(n, sub_expr.collect(), diag),
90-
BuiltinMacroFunction::Markdown => markdown_macro(n, sub_expr.collect()),
9190
}
9291
}
9392

@@ -318,30 +317,6 @@ fn debug_macro(
318317
}
319318
}
320319

321-
fn markdown_macro(node: &dyn Spanned, args: Vec<(Expression, Option<NodeOrToken>)>) -> Expression {
322-
let mut string = None;
323-
for (expr, node) in args {
324-
let escaped = Expression::FunctionCall {
325-
function: BuiltinFunction::EscapeMarkdown.into(),
326-
arguments: vec![expr],
327-
source_location: Some(node.to_source_location()),
328-
};
329-
string = Some(match string {
330-
None => escaped,
331-
Some(string) => Expression::BinaryExpression {
332-
lhs: Box::new(string),
333-
op: '+',
334-
rhs: Box::new(escaped),
335-
},
336-
});
337-
}
338-
Expression::FunctionCall {
339-
function: BuiltinFunction::ParseMarkdown.into(),
340-
arguments: vec![string.unwrap_or_else(|| Expression::default_value_for_type(&Type::String))],
341-
source_location: Some(node.to_source_location()),
342-
}
343-
}
344-
345320
fn to_debug_string(
346321
expr: Expression,
347322
node: &dyn Spanned,

internal/compiler/expression_tree.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,6 @@ pub enum BuiltinMacroFunction {
142142
Hsv,
143143
/// transform `debug(a, b, c)` into debug `a + " " + b + " " + c`
144144
Debug,
145-
/// Markdown
146-
Markdown,
147145
}
148146

149147
macro_rules! declare_builtin_function_types {
@@ -283,7 +281,7 @@ declare_builtin_function_types!(
283281
RestartTimer: (Type::ElementReference) -> Type::Void,
284282
OpenUrl: (Type::String) -> Type::Void,
285283
EscapeMarkdown: (Type::String) -> Type::String,
286-
ParseMarkdown: (Type::String) -> Type::Void
284+
ParseMarkdown: (Type::String) -> Type::String
287285
);
288286

289287
impl BuiltinFunction {

internal/compiler/generator/cpp.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4109,6 +4109,14 @@ fn compile_builtin_function_call(
41094109
let url = a.next().unwrap();
41104110
format!("slint::cbindgen_private::open_url({})", url)
41114111
}
4112+
BuiltinFunction::EscapeMarkdown => {
4113+
let text = a.next().unwrap();
4114+
format!("slint::cbindgen_private::escape_markdown({})", text)
4115+
}
4116+
BuiltinFunction::ParseMarkdown => {
4117+
let text = a.next().unwrap();
4118+
format!("slint::cbindgen_private::parse_markdown({})", text)
4119+
}
41124120
}
41134121
}
41144122

internal/compiler/generator/rust.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3375,6 +3375,14 @@ fn compile_builtin_function_call(
33753375
let url = a.next().unwrap();
33763376
quote!(sp::open_url(&#url))
33773377
}
3378+
BuiltinFunction::EscapeMarkdown => {
3379+
let text = a.next().unwrap();
3380+
quote!(sp::escape_markdown(&#text))
3381+
}
3382+
BuiltinFunction::ParseMarkdown => {
3383+
let text = a.next().unwrap();
3384+
quote!(sp::parse_markdown(&#text))
3385+
}
33783386
}
33793387
}
33803388

internal/compiler/parser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ declare_syntax! {
383383
AtGradient -> [*Expression],
384384
/// `@tr("foo", ...)` // the string is a StringLiteral
385385
AtTr -> [?TrContext, ?TrPlural, *Expression],
386+
AtMarkdown -> [*Expression],
386387
/// `"foo" =>` in a `AtTr` node
387388
TrContext -> [],
388389
/// `| "foo" % n` in a `AtTr` node

internal/compiler/parser/expressions.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ fn parse_at_keyword(p: &mut impl Parser) {
252252
"tr" => {
253253
parse_tr(p);
254254
}
255+
"markdown" => {
256+
parse_markdown(p);
257+
},
255258
_ => {
256259
p.consume();
257260
p.test(SyntaxKind::Identifier); // consume the identifier, so that autocomplete works
@@ -435,6 +438,37 @@ fn parse_tr(p: &mut impl Parser) {
435438
p.expect(SyntaxKind::RParent);
436439
}
437440

441+
fn parse_markdown(p: &mut impl Parser) {
442+
let mut p = p.start_node(SyntaxKind::AtMarkdown);
443+
p.expect(SyntaxKind::At);
444+
debug_assert!(p.peek().as_str().ends_with("markdown"));
445+
p.expect(SyntaxKind::Identifier); //eg "markdown"
446+
p.expect(SyntaxKind::LParent);
447+
448+
fn consume_literal(p: &mut impl Parser) -> bool {
449+
let peek = p.peek();
450+
if peek.kind() != SyntaxKind::StringLiteral
451+
|| !peek.as_str().starts_with('"')
452+
|| !peek.as_str().ends_with('"')
453+
{
454+
p.error("Expected plain string literal");
455+
return false;
456+
}
457+
p.expect(SyntaxKind::StringLiteral)
458+
}
459+
460+
if !consume_literal(&mut *p) {
461+
return;
462+
}
463+
464+
while p.test(SyntaxKind::Comma) {
465+
if !parse_expression(&mut *p) {
466+
break;
467+
}
468+
}
469+
p.expect(SyntaxKind::RParent);
470+
}
471+
438472
#[cfg_attr(test, parser_test)]
439473
/// ```test,AtImageUrl
440474
/// @image-url("foo.png")

internal/compiler/passes/resolving.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ impl Expression {
356356
SyntaxKind::AtImageUrl => Some(Self::from_at_image_url_node(node.into(), ctx)),
357357
SyntaxKind::AtGradient => Some(Self::from_at_gradient(node.into(), ctx)),
358358
SyntaxKind::AtTr => Some(Self::from_at_tr(node.into(), ctx)),
359+
SyntaxKind::AtMarkdown => Some(Self::from_at_markdown(node.into(), ctx)),
359360
SyntaxKind::QualifiedName => Some(Self::from_qualified_name_node(
360361
node.clone().into(),
361362
ctx,
@@ -703,6 +704,126 @@ impl Expression {
703704
}
704705
}
705706
}
707+
708+
fn from_at_markdown(node: syntax_nodes::AtMarkdown, ctx: &mut LookupCtx) -> Expression {
709+
let Some(string) = std::dbg!(node
710+
.child_text(SyntaxKind::StringLiteral))
711+
.and_then(|s| crate::literals::unescape_string(&s))
712+
else {
713+
ctx.diag.push_error("Cannot parse string literal".into(), &node);
714+
return Expression::Invalid;
715+
};
716+
717+
let subs = node.Expression().map(|n| {
718+
Expression::from_expression_node(n.clone(), ctx).maybe_convert_to(
719+
Type::String,
720+
&n,
721+
ctx.diag,
722+
)
723+
});
724+
let values = subs.collect::<Vec<_>>();
725+
std::dbg!(&values);
726+
727+
let mut expr = None;
728+
729+
// check format string
730+
{
731+
let mut arg_idx = 0;
732+
let mut pos_max = 0;
733+
let mut pos = 0;
734+
let mut literal_start_pos = 0;
735+
while let Some(mut p) = string[pos..].find(['{', '}']) {
736+
if string.len() - pos < p + 1 {
737+
ctx.diag.push_error(
738+
"Unescaped trailing '{' in format string. Escape '{' with '{{'".into(),
739+
&node,
740+
);
741+
break;
742+
}
743+
p += pos;
744+
745+
// Skip escaped }
746+
if string.get(p..=p) == Some("}") {
747+
if string.get(p + 1..=p + 1) == Some("}") {
748+
pos = p + 2;
749+
continue;
750+
} else {
751+
ctx.diag.push_error(
752+
"Unescaped '}' in format string. Escape '}' with '}}'".into(),
753+
&node,
754+
);
755+
break;
756+
}
757+
}
758+
759+
// Skip escaped {
760+
if string.get(p + 1..=p + 1) == Some("{") {
761+
pos = p + 2;
762+
continue;
763+
}
764+
765+
// Find the argument
766+
let end = if let Some(end) = string[p..].find('}') {
767+
end + p
768+
} else {
769+
ctx.diag.push_error(
770+
"Unterminated placeholder in format string. '{' must be escaped with '{{'"
771+
.into(),
772+
&node,
773+
);
774+
break;
775+
};
776+
let argument = &string[p + 1..end];
777+
if argument.is_empty() {
778+
arg_idx += 1;
779+
} else if let Ok(n) = argument.parse::<u16>() {
780+
pos_max = pos_max.max(n as usize + 1);
781+
} else {
782+
ctx.diag
783+
.push_error("Invalid '{...}' placeholder in format string. The placeholder must be a number, or braces must be escaped with '{{' and '}}'".into(), &node);
784+
break;
785+
};
786+
let add = Expression::BinaryExpression {
787+
lhs: Box::new(Expression::StringLiteral((&string[literal_start_pos..p]).into())),
788+
op: '+',
789+
rhs: Box::new(values[arg_idx-1].clone())
790+
};
791+
expr = Some(match expr {
792+
None => add,
793+
Some(expr) => Expression::BinaryExpression {
794+
lhs: Box::new(expr),
795+
op: '+',
796+
rhs: Box::new(add),
797+
},
798+
});
799+
pos = end + 1;
800+
literal_start_pos = pos;
801+
}
802+
if arg_idx > 0 && pos_max > 0 {
803+
ctx.diag.push_error(
804+
"Cannot mix positional and non-positional placeholder in format string".into(),
805+
&node,
806+
);
807+
} else if arg_idx > values.len() || pos_max > values.len() {
808+
let num = arg_idx.max(pos_max);
809+
ctx.diag.push_error(
810+
format!("Format string contains {num} placeholders, but only {} extra arguments were given", values.len()),
811+
&node,
812+
);
813+
}
814+
}
815+
816+
std::dbg!(&expr);
817+
818+
Expression::FunctionCall {
819+
function: BuiltinFunction::ParseMarkdown.into(),
820+
arguments: vec![
821+
expr.unwrap()//Expression::StringLiteral(string),
822+
823+
],
824+
source_location: Some(node.to_source_location()),
825+
}
826+
}
706827

707828
fn from_at_tr(node: syntax_nodes::AtTr, ctx: &mut LookupCtx) -> Expression {
708829
let Some(string) = node

internal/core/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,13 @@ pub fn open_url(url: &str) {
172172
debug_log!("Error opening url {}: {}", url, err);
173173
}
174174
}
175+
176+
pub fn escape_markdown(text: &str) -> std::string::String {
177+
std::dbg!(text);
178+
text.into()
179+
}
180+
181+
pub fn parse_markdown(text: &str) -> std::string::String {
182+
std::dbg!(text);
183+
text.into()
184+
}

internal/interpreter/eval.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,16 @@ fn call_builtin_function(
13961396
corelib::open_url(&url);
13971397
Value::Void
13981398
}
1399+
BuiltinFunction::EscapeMarkdown => {
1400+
let text: SharedString =
1401+
eval_expression(&arguments[0], local_context).try_into().unwrap();
1402+
Value::String(corelib::escape_markdown(&text).into())
1403+
}
1404+
BuiltinFunction::ParseMarkdown => {
1405+
let text: SharedString =
1406+
eval_expression(&arguments[0], local_context).try_into().unwrap();
1407+
Value::String(corelib::parse_markdown(&text).into())
1408+
}
13991409
}
14001410
}
14011411

0 commit comments

Comments
 (0)