mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-24 01:02:38 +00:00
Some checks are pending
autofix.ci / format_fix (push) Waiting to run
autofix.ci / lint_typecheck (push) Waiting to run
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, --exclude bevy-example, windows-2022, 1.85) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, macos-14, stable) (push) Blocked by required conditions
CI / tree-sitter (push) Blocked by required conditions
CI / updater_test (0.3.0) (push) Blocked by required conditions
CI / files-changed (push) Waiting to run
CI / build_and_test (--exclude bevy-example, ubuntu-22.04, 1.85) (push) Blocked by required conditions
CI / python_test (windows-2022) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, beta) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, stable) (push) Blocked by required conditions
CI / build_and_test (ubuntu-22.04, nightly) (push) Blocked by required conditions
CI / node_test (macos-14) (push) Blocked by required conditions
CI / node_test (ubuntu-22.04) (push) Blocked by required conditions
CI / node_test (windows-2022) (push) Blocked by required conditions
CI / python_test (macos-14) (push) Blocked by required conditions
CI / python_test (ubuntu-22.04) (push) Blocked by required conditions
CI / cpp_test_driver (macos-13) (push) Blocked by required conditions
CI / cpp_test_driver (ubuntu-22.04) (push) Blocked by required conditions
CI / cpp_test_driver (windows-2022) (push) Blocked by required conditions
CI / cpp_cmake (macos-14, 1.85) (push) Blocked by required conditions
CI / cpp_cmake (ubuntu-22.04, stable) (push) Blocked by required conditions
CI / cpp_cmake (windows-2022, nightly) (push) Blocked by required conditions
CI / cpp_package_test (push) Blocked by required conditions
CI / vsce_build_test (push) Blocked by required conditions
CI / wasm_demo (push) Blocked by required conditions
CI / mcu (pico-st7789, thumbv6m-none-eabi) (push) Blocked by required conditions
CI / mcu (pico2-st7789, thumbv8m.main-none-eabihf) (push) Blocked by required conditions
CI / mcu (stm32h735g, thumbv7em-none-eabihf) (push) Blocked by required conditions
CI / mcu-embassy (push) Blocked by required conditions
CI / ffi_32bit_build (push) Blocked by required conditions
CI / docs (push) Blocked by required conditions
CI / wasm (push) Blocked by required conditions
CI / fmt_test (push) Blocked by required conditions
CI / esp-idf-quick (push) Blocked by required conditions
CI / android (push) Blocked by required conditions
CI / miri (push) Blocked by required conditions
CI / test-figma-inspector (push) Blocked by required conditions
CC: #8747
337 lines
16 KiB
Rust
337 lines
16 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
use crate::common;
|
|
use i_slint_compiler::diagnostics::Spanned;
|
|
use i_slint_compiler::expression_tree::{Callable, Expression};
|
|
use i_slint_compiler::langtype::{ElementType, EnumerationValue, Type};
|
|
use i_slint_compiler::lookup::{LookupObject, LookupResult, LookupResultCallable};
|
|
use i_slint_compiler::namedreference::NamedReference;
|
|
use i_slint_compiler::object_tree::ElementRc;
|
|
use i_slint_compiler::parser::{syntax_nodes, SyntaxKind, SyntaxNode, SyntaxToken};
|
|
use i_slint_compiler::pathutils::clean_path;
|
|
use smol_str::{SmolStr, ToSmolStr};
|
|
use std::path::Path;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum TokenInfo {
|
|
Type(Type),
|
|
ElementType(ElementType),
|
|
ElementRc(ElementRc),
|
|
NamedReference(NamedReference),
|
|
EnumerationValue(EnumerationValue),
|
|
FileName(std::path::PathBuf),
|
|
Image(std::path::PathBuf),
|
|
LocalProperty(syntax_nodes::PropertyDeclaration),
|
|
LocalCallback(syntax_nodes::CallbackDeclaration),
|
|
LocalFunction(syntax_nodes::Function),
|
|
/// This is like a NamedReference, but the element doesn't have an ElementRc because
|
|
/// its enclosing component might not have been properly parsed
|
|
IncompleteNamedReference(ElementType, SmolStr),
|
|
}
|
|
|
|
impl TokenInfo {
|
|
/// Returns the node to the declaration of what the token represents, if it exists
|
|
pub fn declaration(&self) -> Option<SyntaxNode> {
|
|
match self {
|
|
TokenInfo::Type(ty) => match ty {
|
|
Type::Struct(s) if s.node.is_some() => s.node.as_ref().unwrap().parent().clone(),
|
|
Type::Enumeration(e) => e.node.as_deref().cloned(),
|
|
_ => None,
|
|
},
|
|
|
|
TokenInfo::ElementType(el) => {
|
|
if let ElementType::Component(c) = el {
|
|
Some(c.root_element.borrow().debug.first()?.node.clone().into())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
TokenInfo::ElementRc(el) => Some(el.borrow().debug.first()?.node.clone().into()),
|
|
TokenInfo::NamedReference(nr) => {
|
|
let mut el = nr.element();
|
|
loop {
|
|
if let Some(x) = el.borrow().property_declarations.get(nr.name()) {
|
|
return x.node.clone();
|
|
}
|
|
let base = el.borrow().base_type.clone();
|
|
if let ElementType::Component(c) = base {
|
|
el = c.root_element.clone();
|
|
} else {
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
|
|
TokenInfo::EnumerationValue(v) => {
|
|
let enum_node = v.enumeration.node.as_ref()?;
|
|
enum_node.EnumValue().nth(v.value).map(|x| x.clone().into())
|
|
}
|
|
|
|
TokenInfo::FileName(_) | TokenInfo::Image(_) => None,
|
|
|
|
TokenInfo::LocalProperty(x) => Some(x.clone().into()),
|
|
TokenInfo::LocalCallback(x) => Some(x.clone().into()),
|
|
TokenInfo::LocalFunction(x) => Some(x.clone().into()),
|
|
TokenInfo::IncompleteNamedReference(element_type, prop_name) => {
|
|
let mut element_type = element_type.clone();
|
|
while let ElementType::Component(com) = element_type {
|
|
if let Some(p) = com.root_element.borrow().property_declarations.get(prop_name)
|
|
{
|
|
return p.node.clone();
|
|
}
|
|
element_type = com.root_element.borrow().base_type.clone();
|
|
}
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn token_info(document_cache: &common::DocumentCache, token: SyntaxToken) -> Option<TokenInfo> {
|
|
let mut node = token.parent();
|
|
if node.kind() == SyntaxKind::AtImageUrl && token.kind() == SyntaxKind::StringLiteral {
|
|
let path = i_slint_compiler::literals::unescape_string(token.text())?;
|
|
let path = token.source_file.path().parent().map(|p| p.to_path_buf())?.join(path);
|
|
|
|
return Some(TokenInfo::Image(clean_path(&path)));
|
|
}
|
|
|
|
loop {
|
|
if let Some(n) = syntax_nodes::QualifiedName::new(node.clone()) {
|
|
let parent = n.parent()?;
|
|
return match parent.kind() {
|
|
SyntaxKind::Type => {
|
|
let qual = i_slint_compiler::object_tree::QualifiedTypeName::from_node(n);
|
|
let doc = document_cache.get_document_for_source_file(&node.source_file)?;
|
|
Some(TokenInfo::Type(doc.local_registry.lookup_qualified(&qual.members)))
|
|
}
|
|
SyntaxKind::Element => {
|
|
if !crate::common::is_element_node_ignored(&parent.into()) {
|
|
let qual = i_slint_compiler::object_tree::QualifiedTypeName::from_node(n);
|
|
let doc = document_cache.get_document_for_source_file(&node.source_file)?;
|
|
Some(TokenInfo::ElementType(
|
|
doc.local_registry.lookup_element(&qual.to_string()).ok()?,
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
SyntaxKind::Expression => {
|
|
if token.kind() != SyntaxKind::Identifier {
|
|
return None;
|
|
}
|
|
let lr = crate::util::with_lookup_ctx(document_cache, node, None, |ctx| {
|
|
let mut it = n
|
|
.children_with_tokens()
|
|
.filter_map(|t| t.into_token())
|
|
.filter(|t| t.kind() == SyntaxKind::Identifier);
|
|
let mut cur_tok = it.next()?;
|
|
let first_str =
|
|
i_slint_compiler::parser::normalize_identifier(cur_tok.text());
|
|
let global = i_slint_compiler::lookup::global_lookup();
|
|
let mut expr_it = global.lookup(ctx, &first_str)?;
|
|
while cur_tok.token != token.token {
|
|
cur_tok = it.next()?;
|
|
let str =
|
|
i_slint_compiler::parser::normalize_identifier(cur_tok.text());
|
|
expr_it = expr_it.lookup(ctx, &str)?;
|
|
}
|
|
Some(expr_it)
|
|
})?;
|
|
match lr? {
|
|
LookupResult::Expression {
|
|
expression: Expression::ElementReference(e),
|
|
..
|
|
} => Some(TokenInfo::ElementRc(e.upgrade()?)),
|
|
LookupResult::Expression {
|
|
expression: Expression::PropertyReference(nr),
|
|
..
|
|
} => Some(TokenInfo::NamedReference(nr)),
|
|
LookupResult::Expression {
|
|
expression: Expression::EnumerationValue(v),
|
|
..
|
|
} => Some(TokenInfo::EnumerationValue(v)),
|
|
LookupResult::Enumeration(e) => Some(TokenInfo::Type(Type::Enumeration(e))),
|
|
LookupResult::Callable(LookupResultCallable::Callable(
|
|
Callable::Callback(nr) | Callable::Function(nr),
|
|
)) => Some(TokenInfo::NamedReference(nr)),
|
|
_ => return None,
|
|
}
|
|
}
|
|
_ => None,
|
|
};
|
|
} else if let Some(n) = syntax_nodes::ImportIdentifier::new(node.clone()) {
|
|
let doc = document_cache.get_document_for_source_file(&node.source_file)?;
|
|
let imp_name = i_slint_compiler::typeloader::ImportedName::from_node(n);
|
|
return Some(TokenInfo::ElementType(
|
|
doc.local_registry.lookup_element(&imp_name.internal_name).ok()?,
|
|
));
|
|
} else if let Some(n) = syntax_nodes::ExportSpecifier::new(node.clone()) {
|
|
let doc = document_cache.get_document_for_source_file(&node.source_file)?;
|
|
let (_, exp) = i_slint_compiler::object_tree::ExportedName::from_export_specifier(&n);
|
|
return match doc.exports.find(exp.as_str())? {
|
|
itertools::Either::Left(c) => {
|
|
Some(TokenInfo::ElementType(ElementType::Component(c)))
|
|
}
|
|
itertools::Either::Right(ty) => Some(TokenInfo::Type(ty)),
|
|
};
|
|
} else if matches!(node.kind(), SyntaxKind::ImportSpecifier | SyntaxKind::ExportModule) {
|
|
let import_file = node
|
|
.source_file
|
|
.path()
|
|
.parent()
|
|
.unwrap_or_else(|| Path::new("/"))
|
|
.join(node.child_text(SyntaxKind::StringLiteral)?.trim_matches('\"'));
|
|
let import_file = clean_path(&import_file);
|
|
return Some(TokenInfo::FileName(import_file));
|
|
} else if syntax_nodes::BindingExpression::new(node.clone()).is_some() {
|
|
// don't fallback to the Binding
|
|
return None;
|
|
} else if let Some(n) = syntax_nodes::Binding::new(node.clone()) {
|
|
if token.kind() != SyntaxKind::Identifier {
|
|
return None;
|
|
}
|
|
let prop_name = i_slint_compiler::parser::normalize_identifier(token.text());
|
|
let element = syntax_nodes::Element::new(n.parent()?)?;
|
|
if let Some(p) = element.PropertyDeclaration().find_map(|p| {
|
|
(i_slint_compiler::parser::identifier_text(&p.DeclaredIdentifier())? == prop_name)
|
|
.then_some(p)
|
|
}) {
|
|
return Some(TokenInfo::LocalProperty(p));
|
|
}
|
|
return find_property_declaration_in_base(document_cache, element, &prop_name);
|
|
} else if let Some(n) = syntax_nodes::TwoWayBinding::new(node.clone()) {
|
|
if token.kind() != SyntaxKind::Identifier {
|
|
return None;
|
|
}
|
|
let prop_name = i_slint_compiler::parser::normalize_identifier(token.text());
|
|
if prop_name != i_slint_compiler::parser::identifier_text(&n)? {
|
|
return None;
|
|
}
|
|
let element = syntax_nodes::Element::new(n.parent()?)?;
|
|
if let Some(p) = element.PropertyDeclaration().find_map(|p| {
|
|
(i_slint_compiler::parser::identifier_text(&p.DeclaredIdentifier())? == prop_name)
|
|
.then_some(p)
|
|
}) {
|
|
return Some(TokenInfo::LocalProperty(p));
|
|
}
|
|
return find_property_declaration_in_base(document_cache, element, &prop_name);
|
|
} else if let Some(n) = syntax_nodes::CallbackConnection::new(node.clone()) {
|
|
if token.kind() != SyntaxKind::Identifier {
|
|
return None;
|
|
}
|
|
let prop_name = i_slint_compiler::parser::normalize_identifier(token.text());
|
|
if prop_name != i_slint_compiler::parser::identifier_text(&n)? {
|
|
return None;
|
|
}
|
|
let element = syntax_nodes::Element::new(n.parent()?)?;
|
|
if let Some(p) = element.CallbackDeclaration().find_map(|p| {
|
|
(i_slint_compiler::parser::identifier_text(&p.DeclaredIdentifier())? == prop_name)
|
|
.then_some(p)
|
|
}) {
|
|
return Some(TokenInfo::LocalCallback(p));
|
|
}
|
|
return find_property_declaration_in_base(document_cache, element, &prop_name);
|
|
} else if node.kind() == SyntaxKind::DeclaredIdentifier {
|
|
if token.kind() != SyntaxKind::Identifier {
|
|
return None;
|
|
}
|
|
let parent = node.parent()?;
|
|
if matches!(
|
|
parent.kind(),
|
|
SyntaxKind::PropertyChangedCallback | SyntaxKind::PropertyDeclaration
|
|
) {
|
|
let prop_name = i_slint_compiler::parser::normalize_identifier(token.text());
|
|
let element = syntax_nodes::Element::new(parent.parent()?)?;
|
|
if let Some(p) = element.PropertyDeclaration().find_map(|p| {
|
|
(i_slint_compiler::parser::identifier_text(&p.DeclaredIdentifier())?
|
|
== prop_name)
|
|
.then_some(p)
|
|
}) {
|
|
return Some(TokenInfo::LocalProperty(p));
|
|
}
|
|
return find_property_declaration_in_base(document_cache, element, &prop_name);
|
|
}
|
|
if parent.kind() == SyntaxKind::CallbackDeclaration {
|
|
return Some(TokenInfo::LocalCallback(parent.into()));
|
|
}
|
|
if parent.kind() == SyntaxKind::Function {
|
|
return Some(TokenInfo::LocalFunction(parent.into()));
|
|
}
|
|
if parent.kind() == SyntaxKind::Component {
|
|
let doc = document_cache.get_document_for_source_file(&node.source_file)?;
|
|
let element_type = doc
|
|
.local_registry
|
|
.lookup_element(
|
|
i_slint_compiler::parser::normalize_identifier(token.text()).as_str(),
|
|
)
|
|
.ok()?;
|
|
if let ElementType::Component(component) = &element_type {
|
|
if component
|
|
.node
|
|
.as_ref()
|
|
.map(|n| n.text_range().contains_range(token.text_range()))
|
|
.unwrap_or_default()
|
|
{
|
|
return Some(TokenInfo::ElementType(element_type));
|
|
}
|
|
}
|
|
}
|
|
if parent.kind() == SyntaxKind::StructDeclaration {
|
|
let doc = document_cache.get_document_for_source_file(&node.source_file)?;
|
|
let ty = doc
|
|
.local_registry
|
|
.lookup(i_slint_compiler::parser::normalize_identifier(token.text()).as_str());
|
|
match &ty {
|
|
Type::Struct(s)
|
|
if s.node
|
|
.as_ref()
|
|
.and_then(|n| n.parent())
|
|
.map(|n| n.text_range().contains_range(token.text_range()))
|
|
.unwrap_or_default() =>
|
|
{
|
|
return Some(TokenInfo::Type(ty));
|
|
}
|
|
_ => { /* nothing to do */ }
|
|
}
|
|
}
|
|
if parent.kind() == SyntaxKind::EnumDeclaration {
|
|
let doc = document_cache.get_document_for_source_file(&node.source_file)?;
|
|
let ty = doc
|
|
.local_registry
|
|
.lookup(i_slint_compiler::parser::normalize_identifier(token.text()).as_str());
|
|
match &ty {
|
|
Type::Enumeration(e)
|
|
if e.node
|
|
.as_ref()
|
|
.map(|n| n.text_range().contains_range(token.text_range()))
|
|
.unwrap_or_default() =>
|
|
{
|
|
return Some(TokenInfo::Type(ty));
|
|
}
|
|
_ => { /* nothing to do */ }
|
|
}
|
|
}
|
|
return None;
|
|
}
|
|
node = node.parent()?;
|
|
}
|
|
}
|
|
|
|
/// Try to lookup the property `prop_name` in the base of the given Element
|
|
fn find_property_declaration_in_base(
|
|
document_cache: &common::DocumentCache,
|
|
element: syntax_nodes::Element,
|
|
prop_name: &str,
|
|
) -> Option<TokenInfo> {
|
|
let global_tr = document_cache.global_type_registry();
|
|
let tr = element
|
|
.source_file()
|
|
.and_then(|sf| document_cache.get_document_for_source_file(sf))
|
|
.map(|doc| &doc.local_registry)
|
|
.unwrap_or(&global_tr);
|
|
|
|
let element_type = crate::util::lookup_current_element_type((*element).clone(), tr)?;
|
|
Some(TokenInfo::IncompleteNamedReference(element_type, prop_name.to_smolstr()))
|
|
}
|