slint macro: Use Span::local_file when Rust is 1.88
Some checks are pending
autofix.ci / format_fix (push) Waiting to run
autofix.ci / lint_typecheck (push) Waiting to run
CI / docs (push) Blocked by required conditions
CI / wasm (push) Blocked by required conditions
CI / wasm_demo (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 / 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 / files-changed (push) Waiting to run
CI / build_and_test (--exclude bevy-example, ubuntu-22.04, 1.85) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, --exclude bevy-example, windows-2022, 1.85) (push) Blocked by required conditions
CI / python_test (macos-14) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, macos-14, stable) (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 / python_test (ubuntu-22.04) (push) Blocked by required conditions
CI / python_test (windows-2022) (push) Blocked by required conditions
CI / cpp_cmake (macos-14, 1.85) (push) Blocked by required conditions
CI / build_and_test (ubuntu-22.04, nightly) (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 / mcu-embassy (push) Blocked by required conditions
CI / cpp_test_driver (windows-2022) (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 / ffi_32bit_build (push) Blocked by required conditions
CI / cpp_package_test (push) Blocked by required conditions
CI / vsce_build_test (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 / 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

So we can locate the files relative to the .rs file instead of relative
to the Cargo.toml file.

For compatibility with previous version and older rust, still try to
locate files relative to the Cargo.toml for rust files.

The LSP will always auto-complete relative to the .rs file

The live-preview for Rust need to make the file absolute because it
isn't in the case of a macro
This commit is contained in:
Olivier Goffart 2025-09-12 13:27:31 +02:00 committed by GitHub
parent e6f7aaffba
commit 54c378c40b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 31 additions and 19 deletions

View file

@ -23,7 +23,7 @@ default = []
[dependencies]
i-slint-compiler = { workspace = true, features = ["default", "proc_macro_span", "rust", "display-diagnostics"] }
rustversion = "1.0"
proc-macro2 = "1.0.17"
quote = "1.0"
spin_on = { workspace = true }

View file

@ -13,6 +13,7 @@ use i_slint_compiler::parser::SyntaxKind;
use i_slint_compiler::*;
use proc_macro::{Spacing, TokenStream, TokenTree};
use quote::quote;
use std::path::PathBuf;
/// Returns true if the two token are touching. For example the two token `foo`and `-` are touching if
/// it was written like so in the source code: `foo-` but not when written like so `foo -`
@ -374,8 +375,9 @@ fn extract_compiler_config(
/// For the documentation about the syntax of the language, see
#[doc = concat!("[The Slint Language Documentation](https://slint.dev/releases/", env!("CARGO_PKG_VERSION"), "/docs/slint)")]
///
/// When `import`ing `.slint` files or loading images with `@image-url`, the specified paths are relative to the
/// the directory that contains Cargo.toml.
/// When Rust 1.88 or later is used, the paths for loading images with `@image-url` and importing `.slint` files
/// are relative to the `.rs` file that contains the macro.
/// For compatibility with older rust version, the files are also searched in the manifest directory that contains `Cargo.toml`.
///
/// ### Limitations
///
@ -393,7 +395,18 @@ pub fn slint(stream: TokenStream) -> TokenStream {
let mut tokens = vec![];
fill_token_vec(token_iter, &mut tokens);
let source_file = if let Some(cargo_manifest) = std::env::var_os("CARGO_MANIFEST_DIR") {
#[rustversion::since(1.88)]
fn local_file(tokens: &[parser::Token]) -> Option<PathBuf> {
tokens.first()?.span?.local_file()
}
#[rustversion::before(1.88)]
fn local_file(_: &[parser::Token]) -> Option<PathBuf> {
None
}
let source_file = if let Some(path) = local_file(&tokens) {
diagnostics::SourceFileInner::from_path_only(path)
} else if let Some(cargo_manifest) = std::env::var_os("CARGO_MANIFEST_DIR") {
let mut path: std::path::PathBuf = cargo_manifest.into();
path.push("Cargo.toml");
diagnostics::SourceFileInner::from_path_only(path)

View file

@ -31,8 +31,9 @@ pub fn generate(
.as_ref()
.ok_or_else(|| std::io::Error::other("Cannot determine path of the main file"))?
.source_file
.path()
.to_string_lossy();
.path();
let main_file = std::path::absolute(main_file).unwrap_or_else(|_| main_file.to_path_buf());
let main_file = main_file.to_string_lossy();
let public_components = llr
.public_components

View file

@ -1520,8 +1520,9 @@ impl TypeLoader {
) -> Option<(PathBuf, Option<&'static [u8]>)> {
// The directory of the current file is the first in the list of include directories.
referencing_file
.map(base_directory)
.and_then(|x| x.parent().map(|x| x.to_path_buf()))
.into_iter()
.chain(referencing_file.and_then(maybe_base_directory))
.chain(self.compiler_config.include_paths.iter().map(PathBuf::as_path).map(
|include_path| {
let base = referencing_file.map(Path::to_path_buf).unwrap_or_default();
@ -1671,15 +1672,12 @@ fn get_native_style(all_loaded_files: &mut std::collections::BTreeSet<PathBuf>)
i_slint_common::get_native_style(false, &std::env::var("TARGET").unwrap_or_default()).into()
}
/// return the base directory from which imports are loaded
/// For a .rs file, return the manifest directory
///
/// For a .slint file, this is the parent directory.
/// For a .rs file, this is relative to the CARGO_MANIFEST_DIR
///
/// Note: this function is only called for .rs path as part of the LSP or viewer.
/// Because from a proc_macro, we don't actually know the path of the current file, and this
/// is why we must be relative to CARGO_MANIFEST_DIR.
pub fn base_directory(referencing_file: &Path) -> PathBuf {
/// This is for compatibility with `slint!` macro as before rust 1.88,
/// it was not possible for the macro to know the current path and
/// the Cargo.toml file was used instead
fn maybe_base_directory(referencing_file: &Path) -> Option<PathBuf> {
if referencing_file.extension().is_some_and(|e| e == "rs") {
// For .rs file, this is a rust macro, and rust macro locates the file relative to the CARGO_MANIFEST_DIR which is the directory that has a Cargo.toml file.
let mut candidate = referencing_file;
@ -1691,10 +1689,10 @@ pub fn base_directory(referencing_file: &Path) -> PathBuf {
break Some(candidate);
}
}
.map(|x| x.to_path_buf())
} else {
referencing_file.parent()
None
}
.map_or_else(Default::default, |p| p.to_path_buf())
}
#[test]

View file

@ -786,11 +786,11 @@ fn complete_path_in_string(
}
let mut text = text.strip_prefix('\"')?;
text = &text[..(offset - 1)];
let base = i_slint_compiler::typeloader::base_directory(base);
let base = base.parent().unwrap_or(Path::new(""));
let path = if let Some(last_slash) = text.rfind('/') {
base.join(Path::new(&text[..last_slash]))
} else {
base
base.to_owned()
};
let dir = std::fs::read_dir(path).ok()?;
Some(