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] [dependencies]
i-slint-compiler = { workspace = true, features = ["default", "proc_macro_span", "rust", "display-diagnostics"] } i-slint-compiler = { workspace = true, features = ["default", "proc_macro_span", "rust", "display-diagnostics"] }
rustversion = "1.0"
proc-macro2 = "1.0.17" proc-macro2 = "1.0.17"
quote = "1.0" quote = "1.0"
spin_on = { workspace = true } spin_on = { workspace = true }

View file

@ -13,6 +13,7 @@ use i_slint_compiler::parser::SyntaxKind;
use i_slint_compiler::*; use i_slint_compiler::*;
use proc_macro::{Spacing, TokenStream, TokenTree}; use proc_macro::{Spacing, TokenStream, TokenTree};
use quote::quote; 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 /// 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 -` /// 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 /// 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)")] #[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 /// When Rust 1.88 or later is used, the paths for loading images with `@image-url` and importing `.slint` files
/// the directory that contains Cargo.toml. /// 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 /// ### Limitations
/// ///
@ -393,7 +395,18 @@ pub fn slint(stream: TokenStream) -> TokenStream {
let mut tokens = vec![]; let mut tokens = vec![];
fill_token_vec(token_iter, &mut tokens); 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(); let mut path: std::path::PathBuf = cargo_manifest.into();
path.push("Cargo.toml"); path.push("Cargo.toml");
diagnostics::SourceFileInner::from_path_only(path) diagnostics::SourceFileInner::from_path_only(path)

View file

@ -31,8 +31,9 @@ pub fn generate(
.as_ref() .as_ref()
.ok_or_else(|| std::io::Error::other("Cannot determine path of the main file"))? .ok_or_else(|| std::io::Error::other("Cannot determine path of the main file"))?
.source_file .source_file
.path() .path();
.to_string_lossy(); 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 let public_components = llr
.public_components .public_components

View file

@ -1520,8 +1520,9 @@ impl TypeLoader {
) -> Option<(PathBuf, Option<&'static [u8]>)> { ) -> Option<(PathBuf, Option<&'static [u8]>)> {
// The directory of the current file is the first in the list of include directories. // The directory of the current file is the first in the list of include directories.
referencing_file referencing_file
.map(base_directory) .and_then(|x| x.parent().map(|x| x.to_path_buf()))
.into_iter() .into_iter()
.chain(referencing_file.and_then(maybe_base_directory))
.chain(self.compiler_config.include_paths.iter().map(PathBuf::as_path).map( .chain(self.compiler_config.include_paths.iter().map(PathBuf::as_path).map(
|include_path| { |include_path| {
let base = referencing_file.map(Path::to_path_buf).unwrap_or_default(); 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() 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. /// This is for compatibility with `slint!` macro as before rust 1.88,
/// For a .rs file, this is relative to the CARGO_MANIFEST_DIR /// it was not possible for the macro to know the current path and
/// /// the Cargo.toml file was used instead
/// Note: this function is only called for .rs path as part of the LSP or viewer. fn maybe_base_directory(referencing_file: &Path) -> Option<PathBuf> {
/// 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 {
if referencing_file.extension().is_some_and(|e| e == "rs") { 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. // 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; let mut candidate = referencing_file;
@ -1691,10 +1689,10 @@ pub fn base_directory(referencing_file: &Path) -> PathBuf {
break Some(candidate); break Some(candidate);
} }
} }
.map(|x| x.to_path_buf())
} else { } else {
referencing_file.parent() None
} }
.map_or_else(Default::default, |p| p.to_path_buf())
} }
#[test] #[test]

View file

@ -786,11 +786,11 @@ fn complete_path_in_string(
} }
let mut text = text.strip_prefix('\"')?; let mut text = text.strip_prefix('\"')?;
text = &text[..(offset - 1)]; 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('/') { let path = if let Some(last_slash) = text.rfind('/') {
base.join(Path::new(&text[..last_slash])) base.join(Path::new(&text[..last_slash]))
} else { } else {
base base.to_owned()
}; };
let dir = std::fs::read_dir(path).ok()?; let dir = std::fs::read_dir(path).ok()?;
Some( Some(