mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-02 06:41:14 +00:00

Share some code so we can eaily list all possibilities and we will use that in the LSP for the completion
248 lines
10 KiB
Rust
248 lines
10 KiB
Rust
/* LICENSE BEGIN
|
|
This file is part of the SixtyFPS Project -- https://sixtyfps.io
|
|
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
|
|
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
|
|
|
|
SPDX-License-Identifier: GPL-3.0-only
|
|
This file is also available under commercial licensing terms.
|
|
Please contact info@sixtyfps.io for more information.
|
|
LICENSE END */
|
|
|
|
/*!
|
|
Parse the contents of builtins.60 and fill the builtin type registery
|
|
*/
|
|
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::rc::Rc;
|
|
|
|
use crate::expression_tree::Expression;
|
|
use crate::langtype::DefaultSizeBinding;
|
|
use crate::langtype::{BuiltinElement, NativeClass, Type};
|
|
use crate::object_tree::{self, *};
|
|
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNode};
|
|
use crate::typeregister::TypeRegister;
|
|
|
|
/// Parse the contents of builtins.60 and fill the builtin type registery
|
|
/// `register` is the register to fill with the builtin types.
|
|
/// At this point, it really should already contain the basic Types (string, int, ...)
|
|
pub fn load_builtins(register: &mut TypeRegister) {
|
|
let mut diag = crate::diagnostics::BuildDiagnostics::default();
|
|
let node = crate::parser::parse(include_str!("builtins.60").into(), None, &mut diag);
|
|
if !diag.is_empty() {
|
|
let vec = diag.to_string_vec();
|
|
#[cfg(feature = "display-diagnostics")]
|
|
diag.print();
|
|
panic!("Error parsing the builtin elements: {:?}", vec);
|
|
}
|
|
|
|
assert_eq!(node.kind(), crate::parser::SyntaxKind::Document);
|
|
let doc: syntax_nodes::Document = node.into();
|
|
|
|
// parse structs
|
|
for s in doc.StructDeclaration().chain(doc.ExportsList().flat_map(|e| e.StructDeclaration())) {
|
|
let external_name = identifier_text(&s.DeclaredIdentifier()).unwrap();
|
|
let mut ty = object_tree::type_struct_from_node(s.ObjectType(), &mut diag, register);
|
|
if let Type::Struct { name, .. } = &mut ty {
|
|
*name = Some(
|
|
parse_annotation("name", &s.ObjectType())
|
|
.map_or_else(|| external_name.clone(), |s| s.unwrap())
|
|
.to_owned(),
|
|
);
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
register.insert_type_with_name(ty, external_name);
|
|
}
|
|
|
|
let mut natives = HashMap::<String, Rc<BuiltinElement>>::new();
|
|
|
|
let exports = doc
|
|
.ExportsList()
|
|
.flat_map(|e| {
|
|
e.Component()
|
|
.map(|x| {
|
|
let x = identifier_text(&x.DeclaredIdentifier()).unwrap();
|
|
(x.clone(), x)
|
|
})
|
|
.into_iter()
|
|
.chain(e.ExportSpecifier().map(|e| {
|
|
(
|
|
identifier_text(&e.ExportIdentifier()).unwrap(),
|
|
identifier_text(&e.ExportName().unwrap()).unwrap(),
|
|
)
|
|
}))
|
|
})
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
for c in doc.Component().chain(doc.ExportsList().filter_map(|e| e.Component())) {
|
|
let id = identifier_text(&c.DeclaredIdentifier()).unwrap();
|
|
let e = c.Element();
|
|
let diag = RefCell::new(&mut diag);
|
|
let mut n = NativeClass::new_with_properties(
|
|
&id,
|
|
e.PropertyDeclaration()
|
|
.flat_map(|p| {
|
|
if p.TwoWayBinding().is_some() {
|
|
None // aliases are handled further down
|
|
} else {
|
|
Some((
|
|
identifier_text(&p.DeclaredIdentifier()).unwrap(),
|
|
object_tree::type_from_node(p.Type(), *diag.borrow_mut(), register),
|
|
))
|
|
}
|
|
})
|
|
.chain(e.CallbackDeclaration().map(|s| {
|
|
(
|
|
identifier_text(&s.DeclaredIdentifier()).unwrap(),
|
|
Type::Callback {
|
|
args: s
|
|
.Type()
|
|
.map(|a| {
|
|
object_tree::type_from_node(a, *diag.borrow_mut(), register)
|
|
})
|
|
.collect(),
|
|
return_type: s.ReturnType().map(|a| {
|
|
Box::new(object_tree::type_from_node(
|
|
a.Type(),
|
|
*diag.borrow_mut(),
|
|
register,
|
|
))
|
|
}),
|
|
},
|
|
)
|
|
})),
|
|
);
|
|
n.deprecated_aliases = e
|
|
.PropertyDeclaration()
|
|
.flat_map(|p| {
|
|
if let Some(twb) = p.TwoWayBinding() {
|
|
let alias_name = identifier_text(&p.DeclaredIdentifier()).unwrap();
|
|
let alias_target = identifier_text(&twb.Expression().QualifiedName().expect(
|
|
"internal error: built-in aliases can only be declared within the type",
|
|
))
|
|
.unwrap();
|
|
Some((alias_name, alias_target))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
n.cpp_type = parse_annotation("cpp_type", &e).map(|x| x.unwrap());
|
|
n.rust_type_constructor = parse_annotation("rust_type_constructor", &e).map(|x| x.unwrap());
|
|
let global = if let Some(base) = e.QualifiedName() {
|
|
let base = QualifiedTypeName::from_node(base).to_string();
|
|
if base != "_" {
|
|
n.parent = Some(natives.get(&base).unwrap().native_class.clone())
|
|
};
|
|
false
|
|
} else {
|
|
true
|
|
};
|
|
let mut builtin = BuiltinElement::new(Rc::new(n));
|
|
builtin.is_global = global;
|
|
let properties = &builtin.properties;
|
|
builtin.default_bindings.extend(e.PropertyDeclaration().filter_map(|p| {
|
|
let name = identifier_text(&p.DeclaredIdentifier())?;
|
|
let e = p.BindingExpression()?;
|
|
let ty = properties.get(&name).unwrap().clone();
|
|
Some((name, compiled(e, register, ty)))
|
|
}));
|
|
builtin.disallow_global_types_as_child_elements =
|
|
parse_annotation("disallow_global_types_as_child_elements", &e).is_some();
|
|
builtin.is_non_item_type = parse_annotation("is_non_item_type", &e).is_some();
|
|
builtin.accepts_focus = parse_annotation("accepts_focus", &e).is_some();
|
|
builtin.default_size_binding = parse_annotation("default_size_binding", &e)
|
|
.map(|size_type| match size_type.as_ref().map(|s| s.as_str()) {
|
|
Some("expands_to_parent_geometry") => DefaultSizeBinding::ExpandsToParentGeometry,
|
|
Some("implicit_size") => DefaultSizeBinding::ImplicitSize,
|
|
other => panic!("invalid default size binding {:?}", other),
|
|
})
|
|
.unwrap_or(DefaultSizeBinding::None);
|
|
builtin.additional_accepted_child_types = e
|
|
.SubElement()
|
|
.map(|s| {
|
|
let a = identifier_text(&s.Element().QualifiedName().unwrap()).unwrap();
|
|
let t = natives[&a].clone();
|
|
(a, Type::Builtin(t))
|
|
})
|
|
.collect();
|
|
if let Some(builtin_name) = exports.get(&id) {
|
|
if !global {
|
|
builtin.name = builtin_name.clone();
|
|
register
|
|
.insert_type_with_name(Type::Builtin(Rc::new(builtin)), builtin_name.clone());
|
|
} else {
|
|
let glob = Rc::new(Component {
|
|
id: builtin_name.clone(),
|
|
root_element: Rc::new(RefCell::new(Element {
|
|
base_type: Type::Builtin(Rc::new(builtin)),
|
|
..Default::default()
|
|
})),
|
|
..Default::default()
|
|
});
|
|
glob.root_element.borrow_mut().enclosing_component = Rc::downgrade(&glob);
|
|
register.insert_type(Type::Component(glob));
|
|
}
|
|
} else {
|
|
assert!(builtin.default_bindings.is_empty()); // because they are not taken from if we inherit from it
|
|
assert!(builtin.additional_accepted_child_types.is_empty());
|
|
natives.insert(id, Rc::new(builtin));
|
|
}
|
|
}
|
|
|
|
register.property_animation_type = Type::Builtin(natives.remove("PropertyAnimation").unwrap());
|
|
|
|
if !diag.is_empty() {
|
|
let vec = diag.to_string_vec();
|
|
#[cfg(feature = "display-diagnostics")]
|
|
diag.print();
|
|
panic!("Error loading the builtin elements: {:?}", vec);
|
|
}
|
|
}
|
|
|
|
/// Compile an expression, knowing that the expression is basic (does not have lookup to other things)
|
|
fn compiled(
|
|
node: syntax_nodes::BindingExpression,
|
|
type_register: &TypeRegister,
|
|
ty: Type,
|
|
) -> Expression {
|
|
let mut diag = crate::diagnostics::BuildDiagnostics::default();
|
|
let e = Expression::from_binding_expression_node(
|
|
node.clone().into(),
|
|
&mut crate::lookup::LookupCtx::empty_context(type_register, &mut diag),
|
|
)
|
|
.maybe_convert_to(ty, &node, &mut diag);
|
|
if diag.has_error() {
|
|
let vec = diag.to_string_vec();
|
|
#[cfg(feature = "display-diagnostics")]
|
|
diag.print();
|
|
panic!("Error parsing the builtin elements: {:?}", vec);
|
|
}
|
|
e
|
|
}
|
|
|
|
/// Find out if there are comments that starts with `//-key` and returns `None`
|
|
/// if no annotation with this key is found, or `Some(None)` if it is found without a value
|
|
/// or `Some(Some(value))` if there is a `//-key:value` match
|
|
fn parse_annotation(key: &str, node: &SyntaxNode) -> Option<Option<String>> {
|
|
for x in node.children_with_tokens() {
|
|
if x.kind() == SyntaxKind::Comment {
|
|
if let Some(comment) = x
|
|
.as_token()
|
|
.unwrap()
|
|
.text()
|
|
.strip_prefix("//-")
|
|
.and_then(|x| x.trim_end().strip_prefix(key))
|
|
{
|
|
if comment.is_empty() {
|
|
return Some(None);
|
|
}
|
|
if let Some(comment) = comment.strip_prefix(":") {
|
|
return Some(Some(comment.to_owned()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|