mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-03 18:29:09 +00:00
WIP: start working on translations: @tr()
This commit just do the parsing of the @tr macro.
This commit is contained in:
parent
3a4f3c61d5
commit
dcd8450dfa
17 changed files with 161 additions and 52 deletions
|
@ -61,6 +61,7 @@ pub enum BuiltinFunction {
|
|||
RegisterCustomFontByPath,
|
||||
RegisterCustomFontByMemory,
|
||||
RegisterBitmapFont,
|
||||
Translate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -202,6 +203,10 @@ impl BuiltinFunction {
|
|||
BuiltinFunction::RegisterBitmapFont => {
|
||||
Type::Function { return_type: Box::new(Type::Void), args: vec![Type::Int32] }
|
||||
}
|
||||
BuiltinFunction::Translate => Type::Function {
|
||||
return_type: Box::new(Type::String),
|
||||
args: vec![Type::String, Type::Array(Type::String.into())],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,6 +258,7 @@ impl BuiltinFunction {
|
|||
BuiltinFunction::RegisterCustomFontByPath
|
||||
| BuiltinFunction::RegisterCustomFontByMemory
|
||||
| BuiltinFunction::RegisterBitmapFont => false,
|
||||
BuiltinFunction::Translate => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,6 +303,7 @@ impl BuiltinFunction {
|
|||
BuiltinFunction::RegisterCustomFontByPath
|
||||
| BuiltinFunction::RegisterCustomFontByMemory
|
||||
| BuiltinFunction::RegisterBitmapFont => false,
|
||||
BuiltinFunction::Translate => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2833,6 +2833,9 @@ fn compile_builtin_function_call(
|
|||
panic!("internal error: invalid args to ImplicitLayoutInfo {:?}", arguments)
|
||||
}
|
||||
}
|
||||
BuiltinFunction::Translate => {
|
||||
format!("slint::private_api::translate({});", a.join(","))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2441,6 +2441,9 @@ fn compile_builtin_function_call(
|
|||
let window_adapter_tokens = access_window_adapter_field(ctx);
|
||||
quote!(slint::private_unstable_api::re_exports::WindowInner::from_pub(#window_adapter_tokens.window()).set_text_input_focused(#(#a)*))
|
||||
}
|
||||
BuiltinFunction::Translate => {
|
||||
todo!("BuiltinFunction::Translate in rust")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -106,6 +106,7 @@ fn builtin_function_cost(function: &BuiltinFunction) -> isize {
|
|||
BuiltinFunction::DarkColorScheme => isize::MAX,
|
||||
BuiltinFunction::SetTextInputFocused => PROPERTY_ACCESS_COST,
|
||||
BuiltinFunction::TextInputFocused => PROPERTY_ACCESS_COST,
|
||||
BuiltinFunction::Translate => 2 * ALLOC_COST + PROPERTY_ACCESS_COST,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -359,7 +359,7 @@ declare_syntax! {
|
|||
// FIXME: the test should test that as alternative rather than several of them (but it can also be a literal)
|
||||
Expression-> [ ?Expression, ?FunctionCallExpression, ?IndexExpression, ?SelfAssignment,
|
||||
?ConditionalExpression, ?QualifiedName, ?BinaryExpression, ?Array, ?ObjectLiteral,
|
||||
?UnaryOpExpression, ?CodeBlock, ?StringTemplate, ?AtImageUrl, ?AtGradient,
|
||||
?UnaryOpExpression, ?CodeBlock, ?StringTemplate, ?AtImageUrl, ?AtGradient, ?AtTr,
|
||||
?MemberAccess ],
|
||||
/// Concatenate the Expressions to make a string (usually expended from a template string)
|
||||
StringTemplate -> [*Expression],
|
||||
|
@ -367,6 +367,8 @@ declare_syntax! {
|
|||
AtImageUrl -> [],
|
||||
/// `@linear-gradient(...)` or `@radial-gradient(...)`
|
||||
AtGradient -> [*Expression],
|
||||
/// `@tr("foo", ...)` // the string is a StringLiteral
|
||||
AtTr -> [*Expression],
|
||||
/// expression()
|
||||
FunctionCallExpression -> [*Expression],
|
||||
/// `expression[index]`
|
||||
|
|
|
@ -212,6 +212,7 @@ fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence)
|
|||
/// ```test
|
||||
/// @image-url("/foo/bar.png")
|
||||
/// @linear-gradient(0deg, blue, red)
|
||||
/// @tr("foo", bar)
|
||||
/// ```
|
||||
fn parse_at_keyword(p: &mut impl Parser) {
|
||||
debug_assert_eq!(p.peek().kind(), SyntaxKind::At);
|
||||
|
@ -230,10 +231,13 @@ fn parse_at_keyword(p: &mut impl Parser) {
|
|||
"radial-gradient" | "radial_gradient" => {
|
||||
parse_gradient(p);
|
||||
}
|
||||
"tr" => {
|
||||
parse_tr(p);
|
||||
}
|
||||
_ => {
|
||||
p.consume();
|
||||
p.test(SyntaxKind::Identifier); // consume the identifier, so that autocomplete works
|
||||
p.error("Expected 'image-url', 'linear-gradient' or 'radial-gradient' after '@'");
|
||||
p.error("Expected 'image-url', 'tr', 'linear-gradient' or 'radial-gradient' after '@'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -351,3 +355,33 @@ fn parse_gradient(p: &mut impl Parser) {
|
|||
p.test(SyntaxKind::Comma);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(test, parser_test)]
|
||||
/// ```test,AtTr
|
||||
/// @tr("foo")
|
||||
/// @tr("foo", bar)
|
||||
/// ```
|
||||
fn parse_tr(p: &mut impl Parser) {
|
||||
let mut p = p.start_node(SyntaxKind::AtTr);
|
||||
p.expect(SyntaxKind::At);
|
||||
debug_assert_eq!(p.peek().as_str(), "tr");
|
||||
p.expect(SyntaxKind::Identifier); //"tr"
|
||||
p.expect(SyntaxKind::LParent);
|
||||
let peek = p.peek();
|
||||
|
||||
if peek.kind() != SyntaxKind::StringLiteral
|
||||
|| !peek.as_str().starts_with('"')
|
||||
|| !peek.as_str().ends_with('"')
|
||||
{
|
||||
p.error("Expected plain string literal");
|
||||
return;
|
||||
}
|
||||
p.expect(SyntaxKind::StringLiteral);
|
||||
|
||||
while p.test(SyntaxKind::Comma) {
|
||||
if !parse_expression(&mut *p) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
p.expect(SyntaxKind::RParent);
|
||||
}
|
||||
|
|
|
@ -241,6 +241,7 @@ impl Expression {
|
|||
.map(|n| Self::from_expression_node(n, ctx))
|
||||
.or_else(|| node.AtImageUrl().map(|n| Self::from_at_image_url_node(n, ctx)))
|
||||
.or_else(|| node.AtGradient().map(|n| Self::from_at_gradient(n, ctx)))
|
||||
.or_else(|| node.AtTr().map(|n| Self::from_at_tr(n, ctx)))
|
||||
.or_else(|| {
|
||||
node.QualifiedName().map(|n| {
|
||||
let exp =
|
||||
|
@ -515,6 +516,36 @@ impl Expression {
|
|||
}
|
||||
}
|
||||
|
||||
fn from_at_tr(node: syntax_nodes::AtTr, ctx: &mut LookupCtx) -> Expression {
|
||||
let Some(string) = node
|
||||
.child_text(SyntaxKind::StringLiteral)
|
||||
.and_then(|s| crate::literals::unescape_string(&s))
|
||||
else {
|
||||
ctx.diag.push_error("Cannot parse string literal".into(), &node);
|
||||
return Expression::Invalid;
|
||||
};
|
||||
|
||||
let subs = node.Expression().map(|n| {
|
||||
Expression::from_expression_node(n.clone(), ctx).maybe_convert_to(
|
||||
Type::String,
|
||||
&n,
|
||||
ctx.diag,
|
||||
)
|
||||
});
|
||||
|
||||
Expression::FunctionCall {
|
||||
function: Box::new(Expression::BuiltinFunctionReference(
|
||||
BuiltinFunction::Translate,
|
||||
Some(node.to_source_location()),
|
||||
)),
|
||||
arguments: vec![
|
||||
Expression::StringLiteral(string),
|
||||
Expression::Array { element_ty: Type::String, values: subs.collect() },
|
||||
],
|
||||
source_location: Some(node.to_source_location()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform the lookup
|
||||
fn from_qualified_name_node(
|
||||
node: syntax_nodes::QualifiedName,
|
||||
|
|
23
internal/compiler/tests/syntax/basic/tr.slint
Normal file
23
internal/compiler/tests/syntax/basic/tr.slint
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
export component X {
|
||||
property <string> t1: @tr(boo);
|
||||
// ^error{Expected plain string literal}
|
||||
// ^^error{Syntax error: expected ';'}
|
||||
// ^^^error{Parse error}
|
||||
property <string> t2: @tr("boo\{t1}oo");
|
||||
// ^error{Expected plain string literal}
|
||||
// ^^error{Syntax error: expected ';'}
|
||||
|
||||
property <string> t3: @tr("boo" + "foo");
|
||||
// ^error{Syntax error: expected '\)'}
|
||||
// ^^error{Syntax error: expected ';'}
|
||||
|
||||
property <string> t4: @tr("foo{}", t1);
|
||||
|
||||
property <string> t4: @tr("foo{}", t1 t2);
|
||||
// ^error{Syntax error: expected '\)'}
|
||||
// ^^error{Syntax error: expected ';'}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue