mirror of
https://github.com/slint-ui/slint.git
synced 2025-12-23 09:19:32 +00:00
Allow specifying paths for @library imports
This commit is contained in:
parent
df544fe1c9
commit
c5248c005e
32 changed files with 485 additions and 56 deletions
|
|
@ -26,6 +26,7 @@ i-slint-core = { workspace = true, features = ["default"] }
|
|||
slint-interpreter = { workspace = true, features = ["default", "display-diagnostics", "internal"] }
|
||||
spin_on = "0.1"
|
||||
css-color-parser2 = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "2.0.1"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::path::PathBuf;
|
|||
|
||||
use super::JsComponentDefinition;
|
||||
use super::JsDiagnostic;
|
||||
use itertools::Itertools;
|
||||
use slint_interpreter::ComponentCompiler;
|
||||
|
||||
/// ComponentCompiler is the entry point to the Slint interpreter that can be used
|
||||
|
|
@ -26,8 +27,22 @@ impl JsComponentCompiler {
|
|||
}
|
||||
None => vec![],
|
||||
};
|
||||
let library_paths = match std::env::var_os("SLINT_LIBRARY_PATH") {
|
||||
Some(paths) => std::env::split_paths(&paths)
|
||||
.filter_map(|entry| {
|
||||
entry
|
||||
.to_str()
|
||||
.unwrap_or_default()
|
||||
.split('=')
|
||||
.collect_tuple()
|
||||
.map(|(k, v)| (k.into(), v.into()))
|
||||
})
|
||||
.collect(),
|
||||
None => std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
compiler.set_include_paths(include_paths);
|
||||
compiler.set_library_paths(library_paths);
|
||||
Self { internal: compiler }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ vtable = { version = "0.1.6", path="../../../helper_crates/vtable" }
|
|||
|
||||
css-color-parser2 = { workspace = true }
|
||||
generativity = "1"
|
||||
itertools = { workspace = true }
|
||||
neon = "0.8.0"
|
||||
once_cell = "1.5"
|
||||
rand = "0.8"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use i_slint_compiler::langtype::Type;
|
|||
use i_slint_core::model::{Model, ModelRc};
|
||||
use i_slint_core::window::WindowInner;
|
||||
use i_slint_core::{ImageInner, SharedVector};
|
||||
use itertools::Itertools;
|
||||
use neon::prelude::*;
|
||||
use rand::RngCore;
|
||||
use slint_interpreter::ComponentHandle;
|
||||
|
|
@ -59,8 +60,22 @@ fn load(mut cx: FunctionContext) -> JsResult<JsValue> {
|
|||
}
|
||||
None => vec![],
|
||||
};
|
||||
let library_paths = match std::env::var_os("SLINT_LIBRARY_PATH") {
|
||||
Some(paths) => std::env::split_paths(&paths)
|
||||
.filter_map(|entry| {
|
||||
entry
|
||||
.to_str()
|
||||
.unwrap_or_default()
|
||||
.split('=')
|
||||
.collect_tuple()
|
||||
.map(|(k, v)| (k.into(), v.into()))
|
||||
})
|
||||
.collect(),
|
||||
None => std::collections::HashMap::new(),
|
||||
};
|
||||
let mut compiler = slint_interpreter::ComponentCompiler::default();
|
||||
compiler.set_include_paths(include_paths);
|
||||
compiler.set_library_paths(library_paths);
|
||||
let c = spin_on::spin_on(compiler.build_from_path(path));
|
||||
|
||||
slint_interpreter::print_diagnostics(compiler.diagnostics());
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ compile_error!(
|
|||
forward compatibility with future version of this crate"
|
||||
);
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
|
@ -101,6 +102,39 @@ impl CompilerConfiguration {
|
|||
Self { config }
|
||||
}
|
||||
|
||||
/// Create a new configuration that sets the library paths used for looking up
|
||||
/// `@library` imports to the specified map of paths.
|
||||
///
|
||||
/// Each library path can either be a path to a `.slint` file or a directory.
|
||||
/// If it's a file, the library is imported by its name prefixed by `@` (e.g.
|
||||
/// `@example`). The specified file is the only entry-point for the library
|
||||
/// and other files from the library won't be accessible from the outside.
|
||||
/// If it's a directory, a specific file in that directory must be specified
|
||||
/// when importing the library (e.g. `@example/widgets.slint`). This allows
|
||||
/// exposing multiple entry-points for a single library.
|
||||
///
|
||||
/// Compile `ui/main.slint` and specify an "example" library path:
|
||||
/// ```rust,no_run
|
||||
/// let manifest_dir = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||
/// let library_paths = std::collections::HashMap::from([(
|
||||
/// "example".to_string(),
|
||||
/// manifest_dir.join("third_party/example/ui/lib.slint"),
|
||||
/// )]);
|
||||
/// let config = slint_build::CompilerConfiguration::new().with_library_paths(library_paths);
|
||||
/// slint_build::compile_with_config("ui/main.slint", config).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// Import the "example" library in `ui/main.slint`:
|
||||
/// ```slint,ignore
|
||||
/// import { Example } from "@example";
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn with_library_paths(self, library_paths: HashMap<String, std::path::PathBuf>) -> Self {
|
||||
let mut config = self.config;
|
||||
config.library_paths = library_paths;
|
||||
Self { config }
|
||||
}
|
||||
|
||||
/// Create a new configuration that selects the style to be used for widgets.
|
||||
#[must_use]
|
||||
pub fn with_style(self, style: String) -> Self {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
extern crate proc_macro;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use i_slint_compiler::diagnostics::BuildDiagnostics;
|
||||
use i_slint_compiler::parser::SyntaxKind;
|
||||
use i_slint_compiler::*;
|
||||
|
|
@ -257,10 +259,24 @@ fn fill_token_vec(stream: impl Iterator<Item = TokenTree>, vec: &mut Vec<parser:
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_include_paths(
|
||||
fn extract_path(literal: proc_macro::Literal) -> std::path::PathBuf {
|
||||
let path_with_quotes = literal.to_string();
|
||||
let path_with_quotes_stripped = if let Some(p) = path_with_quotes.strip_prefix('r') {
|
||||
let hash_removed = p.trim_matches('#');
|
||||
hash_removed.strip_prefix('\"').unwrap().strip_suffix('\"').unwrap()
|
||||
} else {
|
||||
// FIXME: unescape
|
||||
path_with_quotes.trim_matches('\"')
|
||||
};
|
||||
path_with_quotes_stripped.into()
|
||||
}
|
||||
|
||||
fn extract_include_and_library_paths(
|
||||
mut stream: proc_macro::token_stream::IntoIter,
|
||||
) -> (impl Iterator<Item = TokenTree>, Vec<std::path::PathBuf>) {
|
||||
) -> (impl Iterator<Item = TokenTree>, Vec<std::path::PathBuf>, HashMap<String, std::path::PathBuf>)
|
||||
{
|
||||
let mut include_paths = Vec::new();
|
||||
let mut library_paths = HashMap::new();
|
||||
|
||||
let mut remaining_stream;
|
||||
loop {
|
||||
|
|
@ -270,24 +286,36 @@ fn extract_include_paths(
|
|||
if p.as_char() == '#' && group.delimiter() == proc_macro::Delimiter::Bracket =>
|
||||
{
|
||||
let mut attr_stream = group.stream().into_iter();
|
||||
match (attr_stream.next(), attr_stream.next(), attr_stream.next()) {
|
||||
(
|
||||
Some(TokenTree::Ident(include_ident)),
|
||||
Some(TokenTree::Punct(equal_punct)),
|
||||
Some(TokenTree::Literal(path)),
|
||||
) if include_ident.to_string() == "include_path"
|
||||
&& equal_punct.as_char() == '=' =>
|
||||
match attr_stream.next() {
|
||||
Some(TokenTree::Ident(include_ident))
|
||||
if include_ident.to_string() == "include_path" =>
|
||||
{
|
||||
let path_with_quotes = path.to_string();
|
||||
let path_with_quotes_stripped =
|
||||
if let Some(p) = path_with_quotes.strip_prefix('r') {
|
||||
let hash_removed = p.trim_matches('#');
|
||||
hash_removed.strip_prefix('\"').unwrap().strip_suffix('\"').unwrap()
|
||||
} else {
|
||||
// FIXME: unescape
|
||||
path_with_quotes.trim_matches('\"')
|
||||
};
|
||||
include_paths.push(path_with_quotes_stripped.into());
|
||||
match (attr_stream.next(), attr_stream.next()) {
|
||||
(
|
||||
Some(TokenTree::Punct(equal_punct)),
|
||||
Some(TokenTree::Literal(path)),
|
||||
) if equal_punct.as_char() == '=' => {
|
||||
include_paths.push(extract_path(path));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
Some(TokenTree::Ident(library_ident))
|
||||
if library_ident.to_string() == "library_path" =>
|
||||
{
|
||||
match (attr_stream.next(), attr_stream.next(), attr_stream.next()) {
|
||||
(
|
||||
Some(TokenTree::Group(group)),
|
||||
Some(TokenTree::Punct(equal_punct)),
|
||||
Some(TokenTree::Literal(path)),
|
||||
) if group.delimiter() == proc_macro::Delimiter::Parenthesis
|
||||
&& equal_punct.as_char() == '=' =>
|
||||
{
|
||||
let library_name = group.stream().into_iter().next().unwrap();
|
||||
library_paths.insert(library_name.to_string(), extract_path(path));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
|
|
@ -295,7 +323,7 @@ fn extract_include_paths(
|
|||
_ => break,
|
||||
}
|
||||
}
|
||||
(remaining_stream, include_paths)
|
||||
(remaining_stream, include_paths, library_paths)
|
||||
}
|
||||
|
||||
/// This macro allows you to use the Slint design markup language inline in Rust code. Within the braces of the macro
|
||||
|
|
@ -315,7 +343,7 @@ fn extract_include_paths(
|
|||
pub fn slint(stream: TokenStream) -> TokenStream {
|
||||
let token_iter = stream.into_iter();
|
||||
|
||||
let (token_iter, include_paths) = extract_include_paths(token_iter);
|
||||
let (token_iter, include_paths, library_paths) = extract_include_and_library_paths(token_iter);
|
||||
|
||||
let mut tokens = vec![];
|
||||
fill_token_vec(token_iter, &mut tokens);
|
||||
|
|
@ -338,6 +366,7 @@ pub fn slint(stream: TokenStream) -> TokenStream {
|
|||
CompilerConfiguration::new(i_slint_compiler::generator::OutputFormat::Rust);
|
||||
compiler_config.translation_domain = std::env::var("CARGO_PKG_NAME").ok();
|
||||
compiler_config.include_paths = include_paths;
|
||||
compiler_config.library_paths = library_paths;
|
||||
let (root_component, diag) =
|
||||
spin_on::spin_on(compile_syntax_node(syntax_node, diag, compiler_config));
|
||||
//println!("{:#?}", tree);
|
||||
|
|
|
|||
|
|
@ -113,6 +113,15 @@
|
|||
"type": "string"
|
||||
},
|
||||
"description": "List of paths in which the `import` statement and `@image-url` are looked up"
|
||||
},
|
||||
"slint.libraryPaths": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z][a-zA-Z0-9-_]*$": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "Map of paths in which the `import` statement for `@library` imports are looked up"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ extern crate proc_macro;
|
|||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub mod builtin_macros;
|
||||
|
|
@ -56,6 +57,8 @@ pub struct CompilerConfiguration {
|
|||
pub embed_resources: EmbedResourcesKind,
|
||||
/// The compiler will look in these paths for components used in the file to compile.
|
||||
pub include_paths: Vec<std::path::PathBuf>,
|
||||
/// The compiler will look in these paths for library imports.
|
||||
pub library_paths: HashMap<String, std::path::PathBuf>,
|
||||
/// the name of the style. (eg: "native")
|
||||
pub style: Option<String>,
|
||||
|
||||
|
|
@ -140,6 +143,7 @@ impl CompilerConfiguration {
|
|||
Self {
|
||||
embed_resources,
|
||||
include_paths: Default::default(),
|
||||
library_paths: Default::default(),
|
||||
style: Default::default(),
|
||||
open_import_fallback: None,
|
||||
resource_url_mapper: None,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
import { SubType } from "./dependency_local.slint";
|
||||
import { AnotherType } from "dependency_from_incpath.slint";
|
||||
import { LibraryType } from "@library";
|
||||
|
||||
export Main := Rectangle {
|
||||
SubType {}
|
||||
AnotherType {}
|
||||
LibraryType {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||
|
||||
export component LibraryType inherits Rectangle {}
|
||||
5
internal/compiler/tests/typeloader/library/lib.slint
Normal file
5
internal/compiler/tests/typeloader/library/lib.slint
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||
|
||||
import { LibraryType } from "./dependency_from_library.slint";
|
||||
export { LibraryType }
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||
|
||||
export component LibraryHelperType inherits Rectangle {}
|
||||
|
|
@ -4,9 +4,11 @@
|
|||
slint!(
|
||||
import { SubType } from "./tests/typeloader/dependency_local.slint";
|
||||
import { AnotherType } from "dependency_from_incpath.slint";
|
||||
import { LibraryType } from "@library";
|
||||
|
||||
export component Main {
|
||||
SubType {}
|
||||
AnotherType {}
|
||||
LibraryType {}
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use crate::typeregister::TypeRegister;
|
|||
use crate::CompilerConfiguration;
|
||||
use crate::{fileaccess, parser};
|
||||
use core::future::Future;
|
||||
use itertools::Itertools;
|
||||
|
||||
/// Storage for a cache of all loaded documents
|
||||
#[derive(Default)]
|
||||
|
|
@ -149,7 +150,12 @@ impl TypeLoader {
|
|||
let mut foreign_imports = vec![];
|
||||
let mut dependencies = Self::collect_dependencies(state, doc)
|
||||
.filter_map(|mut import| {
|
||||
if import.file.ends_with(".60") || import.file.ends_with(".slint") {
|
||||
let resolved_import = if let Some((path, _)) = state.borrow().tl.resolve_import_path(Some(&import.import_uri_token.clone().into()), &import.file) {
|
||||
path.to_string_lossy().to_string()
|
||||
} else {
|
||||
import.file.clone()
|
||||
};
|
||||
if resolved_import.ends_with(".slint") || resolved_import.ends_with(".60") || import.file.starts_with('@') {
|
||||
Some(Box::pin(async move {
|
||||
let file = import.file.as_str();
|
||||
let doc_path = Self::ensure_document_loaded(
|
||||
|
|
@ -192,10 +198,7 @@ impl TypeLoader {
|
|||
}
|
||||
}))
|
||||
} else {
|
||||
if let Some((path, _)) = state.borrow().tl.resolve_import_path(Some(&import.import_uri_token.clone().into()), &import.file) {
|
||||
import.file = path.to_string_lossy().to_string();
|
||||
};
|
||||
|
||||
import.file = resolved_import;
|
||||
foreign_imports.push(import);
|
||||
None
|
||||
}
|
||||
|
|
@ -258,22 +261,24 @@ impl TypeLoader {
|
|||
import_token: Option<&NodeOrToken>,
|
||||
maybe_relative_path_or_url: &str,
|
||||
) -> Option<(PathBuf, Option<&'static [u8]>)> {
|
||||
let referencing_file_or_url =
|
||||
import_token.and_then(|tok| tok.source_file().map(|s| s.path()));
|
||||
|
||||
self.find_file_in_include_path(referencing_file_or_url, maybe_relative_path_or_url).or_else(
|
||||
|| {
|
||||
referencing_file_or_url
|
||||
.and_then(|base_path_or_url| {
|
||||
crate::pathutils::join(
|
||||
&crate::pathutils::dirname(base_path_or_url),
|
||||
&PathBuf::from(maybe_relative_path_or_url),
|
||||
)
|
||||
})
|
||||
.filter(|p| p.exists())
|
||||
.map(|p| (p, None))
|
||||
},
|
||||
)
|
||||
if let Some(maybe_library_import) = maybe_relative_path_or_url.strip_prefix('@') {
|
||||
self.find_file_in_library_path(maybe_library_import)
|
||||
} else {
|
||||
let referencing_file_or_url =
|
||||
import_token.and_then(|tok| tok.source_file().map(|s| s.path()));
|
||||
self.find_file_in_include_path(referencing_file_or_url, maybe_relative_path_or_url)
|
||||
.or_else(|| {
|
||||
referencing_file_or_url
|
||||
.and_then(|base_path_or_url| {
|
||||
crate::pathutils::join(
|
||||
&crate::pathutils::dirname(base_path_or_url),
|
||||
&PathBuf::from(maybe_relative_path_or_url),
|
||||
)
|
||||
})
|
||||
.filter(|p| p.exists())
|
||||
.map(|p| (p, None))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn ensure_document_loaded<'a: 'b, 'b>(
|
||||
|
|
@ -384,9 +389,15 @@ impl TypeLoader {
|
|||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
state.borrow_mut().diag.push_error(
|
||||
format!(
|
||||
"Cannot find requested import \"{file_to_import}\" in the include search path",
|
||||
),
|
||||
if file_to_import.starts_with('@') {
|
||||
format!(
|
||||
"Cannot find requested import \"{file_to_import}\" in the library search path",
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"Cannot find requested import \"{file_to_import}\" in the include search path",
|
||||
)
|
||||
},
|
||||
&import_token,
|
||||
);
|
||||
false
|
||||
|
|
@ -526,6 +537,28 @@ impl TypeLoader {
|
|||
}
|
||||
}
|
||||
|
||||
/// Lookup a library and filename and try to find the absolute filename based on the library path
|
||||
fn find_file_in_library_path(
|
||||
&self,
|
||||
maybe_library_import: &str,
|
||||
) -> Option<(PathBuf, Option<&'static [u8]>)> {
|
||||
let (library, file) = maybe_library_import
|
||||
.splitn(2, '/')
|
||||
.collect_tuple()
|
||||
.map(|(library, path)| (library, Some(path)))
|
||||
.unwrap_or((maybe_library_import, None));
|
||||
self.compiler_config.library_paths.get(library).and_then(|library_path| {
|
||||
let path = match file {
|
||||
// "@library/file.slint" -> "/path/to/library/" + "file.slint"
|
||||
Some(file) => library_path.join(file),
|
||||
// "@library" -> "/path/to/library/lib.slint"
|
||||
None => library_path.clone(),
|
||||
};
|
||||
crate::fileaccess::load_file(path.as_path())
|
||||
.map(|virtual_file| (virtual_file.canon_path, virtual_file.builtin_contents))
|
||||
})
|
||||
}
|
||||
|
||||
/// Lookup a filename and try to find the absolute filename based on the include path or
|
||||
/// the current file directory
|
||||
pub fn find_file_in_include_path(
|
||||
|
|
@ -693,6 +726,8 @@ fn test_dependency_loading() {
|
|||
let mut compiler_config =
|
||||
CompilerConfiguration::new(crate::generator::OutputFormat::Interpreter);
|
||||
compiler_config.include_paths = vec![incdir];
|
||||
compiler_config.library_paths =
|
||||
HashMap::from([("library".into(), test_source_path.join("library").join("lib.slint"))]);
|
||||
compiler_config.style = Some("fluent".into());
|
||||
|
||||
let mut main_test_path = test_source_path;
|
||||
|
|
@ -711,7 +746,7 @@ fn test_dependency_loading() {
|
|||
|
||||
let mut loader = TypeLoader::new(global_registry, compiler_config, &mut build_diagnostics);
|
||||
|
||||
spin_on::spin_on(loader.load_dependencies_recursively(
|
||||
let (foreign_imports, _) = spin_on::spin_on(loader.load_dependencies_recursively(
|
||||
&doc_node,
|
||||
&mut build_diagnostics,
|
||||
®istry,
|
||||
|
|
@ -719,6 +754,7 @@ fn test_dependency_loading() {
|
|||
|
||||
assert!(!test_diags.has_error());
|
||||
assert!(!build_diagnostics.has_error());
|
||||
assert!(foreign_imports.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -732,6 +768,8 @@ fn test_dependency_loading_from_rust() {
|
|||
let mut compiler_config =
|
||||
CompilerConfiguration::new(crate::generator::OutputFormat::Interpreter);
|
||||
compiler_config.include_paths = vec![incdir];
|
||||
compiler_config.library_paths =
|
||||
HashMap::from([("library".into(), test_source_path.join("library").join("lib.slint"))]);
|
||||
compiler_config.style = Some("fluent".into());
|
||||
|
||||
let mut main_test_path = test_source_path;
|
||||
|
|
@ -750,7 +788,7 @@ fn test_dependency_loading_from_rust() {
|
|||
|
||||
let mut loader = TypeLoader::new(global_registry, compiler_config, &mut build_diagnostics);
|
||||
|
||||
spin_on::spin_on(loader.load_dependencies_recursively(
|
||||
let (foreign_imports, _) = spin_on::spin_on(loader.load_dependencies_recursively(
|
||||
&doc_node,
|
||||
&mut build_diagnostics,
|
||||
®istry,
|
||||
|
|
@ -758,6 +796,7 @@ fn test_dependency_loading_from_rust() {
|
|||
|
||||
assert!(!test_diags.has_error());
|
||||
assert!(!build_diagnostics.has_error());
|
||||
assert!(foreign_imports.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -933,3 +972,104 @@ fn test_unknown_style() {
|
|||
assert_eq!(diags.len(), 1);
|
||||
assert!(diags[0].starts_with("Style FooBar in not known. Use one of the builtin styles ["));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_library_import() {
|
||||
let test_source_path: PathBuf =
|
||||
[env!("CARGO_MANIFEST_DIR"), "tests", "typeloader", "library"].iter().collect();
|
||||
|
||||
let library_paths = HashMap::from([
|
||||
("libdir".into(), test_source_path.clone()),
|
||||
("libfile.slint".into(), test_source_path.join("lib.slint")),
|
||||
]);
|
||||
|
||||
let mut compiler_config =
|
||||
CompilerConfiguration::new(crate::generator::OutputFormat::Interpreter);
|
||||
compiler_config.library_paths = library_paths;
|
||||
compiler_config.style = Some("fluent".into());
|
||||
let mut test_diags = crate::diagnostics::BuildDiagnostics::default();
|
||||
|
||||
let doc_node = crate::parser::parse(
|
||||
r#"
|
||||
/* ... */
|
||||
import { LibraryType } from "@libfile.slint";
|
||||
import { LibraryHelperType } from "@libdir/library_helper_type.slint";
|
||||
"#
|
||||
.into(),
|
||||
Some(std::path::Path::new("HELLO")),
|
||||
&mut test_diags,
|
||||
);
|
||||
|
||||
let doc_node: syntax_nodes::Document = doc_node.into();
|
||||
let global_registry = TypeRegister::builtin();
|
||||
let registry = Rc::new(RefCell::new(TypeRegister::new(&global_registry)));
|
||||
let mut build_diagnostics = BuildDiagnostics::default();
|
||||
let mut loader = TypeLoader::new(global_registry, compiler_config, &mut build_diagnostics);
|
||||
spin_on::spin_on(loader.load_dependencies_recursively(
|
||||
&doc_node,
|
||||
&mut build_diagnostics,
|
||||
®istry,
|
||||
));
|
||||
assert!(!test_diags.has_error());
|
||||
assert!(!build_diagnostics.has_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_library_import_errors() {
|
||||
let test_source_path: PathBuf =
|
||||
[env!("CARGO_MANIFEST_DIR"), "tests", "typeloader", "library"].iter().collect();
|
||||
|
||||
let library_paths = HashMap::from([
|
||||
("libdir".into(), test_source_path.clone()),
|
||||
("libfile.slint".into(), test_source_path.join("lib.slint")),
|
||||
]);
|
||||
|
||||
let mut compiler_config =
|
||||
CompilerConfiguration::new(crate::generator::OutputFormat::Interpreter);
|
||||
compiler_config.library_paths = library_paths;
|
||||
compiler_config.style = Some("fluent".into());
|
||||
let mut test_diags = crate::diagnostics::BuildDiagnostics::default();
|
||||
|
||||
let doc_node = crate::parser::parse(
|
||||
r#"
|
||||
/* ... */
|
||||
import { A } from "@libdir";
|
||||
import { B } from "@libdir/unknown.slint";
|
||||
import { C } from "@libfile.slint/unknown.slint";
|
||||
import { D } from "@unknown";
|
||||
import { E } from "@unknown/lib.slint";
|
||||
"#
|
||||
.into(),
|
||||
Some(std::path::Path::new("HELLO")),
|
||||
&mut test_diags,
|
||||
);
|
||||
|
||||
let doc_node: syntax_nodes::Document = doc_node.into();
|
||||
let global_registry = TypeRegister::builtin();
|
||||
let registry = Rc::new(RefCell::new(TypeRegister::new(&global_registry)));
|
||||
let mut build_diagnostics = BuildDiagnostics::default();
|
||||
let mut loader = TypeLoader::new(global_registry, compiler_config, &mut build_diagnostics);
|
||||
spin_on::spin_on(loader.load_dependencies_recursively(
|
||||
&doc_node,
|
||||
&mut build_diagnostics,
|
||||
®istry,
|
||||
));
|
||||
assert!(!test_diags.has_error());
|
||||
assert!(build_diagnostics.has_error());
|
||||
let diags = build_diagnostics.to_string_vec();
|
||||
assert_eq!(diags.len(), 5);
|
||||
assert!(diags[0].starts_with(&format!(
|
||||
"HELLO:3: Error reading requested import \"{}\": ",
|
||||
test_source_path.to_string_lossy()
|
||||
)));
|
||||
assert_eq!(&diags[1], "HELLO:4: Cannot find requested import \"@libdir/unknown.slint\" in the library search path");
|
||||
assert_eq!(&diags[2], "HELLO:5: Cannot find requested import \"@libfile.slint/unknown.slint\" in the library search path");
|
||||
assert_eq!(
|
||||
&diags[3],
|
||||
"HELLO:6: Cannot find requested import \"@unknown\" in the library search path"
|
||||
);
|
||||
assert_eq!(
|
||||
&diags[4],
|
||||
"HELLO:7: Cannot find requested import \"@unknown/lib.slint\" in the library search path"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -537,6 +537,16 @@ impl ComponentCompiler {
|
|||
&self.config.include_paths
|
||||
}
|
||||
|
||||
/// Sets the library paths used for looking up `@library` imports to the specified map of library names to paths.
|
||||
pub fn set_library_paths(&mut self, library_paths: HashMap<String, PathBuf>) {
|
||||
self.config.library_paths = library_paths;
|
||||
}
|
||||
|
||||
/// Returns the library paths the component compiler is currently configured with.
|
||||
pub fn library_paths(&self) -> &HashMap<String, PathBuf> {
|
||||
&self.config.library_paths
|
||||
}
|
||||
|
||||
/// Sets the style to be used for widgets.
|
||||
///
|
||||
/// Use the "material" style as widget style when compiling:
|
||||
|
|
|
|||
12
tests/cases/imports/library.slint
Normal file
12
tests/cases/imports/library.slint
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||
|
||||
//library_path(helper_components): ../../helper_components/
|
||||
//library_path(helper_buttons): ../../helper_components/test_button.slint
|
||||
import { TestButton } from "@helper_components/test_button.slint";
|
||||
import { ColorButton } from "@helper_buttons";
|
||||
|
||||
export component TestCase {
|
||||
TestButton {}
|
||||
ColorButton {}
|
||||
}
|
||||
|
|
@ -12,11 +12,15 @@ pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box<dyn Error>>
|
|||
let include_paths = test_driver_lib::extract_include_paths(&source)
|
||||
.map(std::path::PathBuf::from)
|
||||
.collect::<Vec<_>>();
|
||||
let library_paths = test_driver_lib::extract_library_paths(&source)
|
||||
.map(|(k, v)| (k.to_string(), std::path::PathBuf::from(v)))
|
||||
.collect::<std::collections::HashMap<_, _>>();
|
||||
|
||||
let mut diag = BuildDiagnostics::default();
|
||||
let syntax_node = parser::parse(source.clone(), Some(&testcase.absolute_path), &mut diag);
|
||||
let mut compiler_config = CompilerConfiguration::new(generator::OutputFormat::Cpp);
|
||||
compiler_config.include_paths = include_paths;
|
||||
compiler_config.library_paths = library_paths;
|
||||
let (root_component, diag) =
|
||||
spin_on::spin_on(compile_syntax_node(syntax_node, diag, compiler_config));
|
||||
|
||||
|
|
|
|||
|
|
@ -146,6 +146,34 @@ fn test_extract_include_paths() {
|
|||
assert_eq!(r, ["../first", "../second"]);
|
||||
}
|
||||
|
||||
/// Extract extra library paths from a comment in the source if present.
|
||||
pub fn extract_library_paths(source: &str) -> impl Iterator<Item = (&'_ str, &'_ str)> {
|
||||
lazy_static::lazy_static! {
|
||||
static ref RX: Regex = Regex::new(r"//library_path\((.+)\):\s*(.+)\s*\n").unwrap();
|
||||
}
|
||||
RX.captures_iter(source)
|
||||
.map(|mat| (mat.get(1).unwrap().as_str().trim(), mat.get(2).unwrap().as_str().trim()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_library_paths() {
|
||||
use std::collections::HashMap;
|
||||
|
||||
assert!(extract_library_paths("something").next().is_none());
|
||||
|
||||
let source = r"
|
||||
//library_path(first): ../first/lib.slint
|
||||
//library_path(second): ../second/lib.slint
|
||||
Blah {}
|
||||
";
|
||||
|
||||
let r = extract_library_paths(source).collect::<HashMap<_, _>>();
|
||||
assert_eq!(
|
||||
r,
|
||||
HashMap::from([("first", "../first/lib.slint"), ("second", "../second/lib.slint")])
|
||||
);
|
||||
}
|
||||
|
||||
/// Extract `//ignore` comments from the source.
|
||||
fn extract_ignores(source: &str) -> impl Iterator<Item = &'_ str> {
|
||||
lazy_static::lazy_static! {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use itertools::Itertools;
|
||||
use slint_interpreter::{DiagnosticLevel, Value, ValueType};
|
||||
use std::error::Error;
|
||||
use std::{collections::HashMap, error::Error};
|
||||
|
||||
pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box<dyn Error>> {
|
||||
i_slint_backend_testing::init();
|
||||
|
|
@ -12,8 +12,12 @@ pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box<dyn Error>>
|
|||
let include_paths = test_driver_lib::extract_include_paths(&source)
|
||||
.map(std::path::PathBuf::from)
|
||||
.collect::<Vec<_>>();
|
||||
let library_paths = test_driver_lib::extract_library_paths(&source)
|
||||
.map(|(k, v)| (k.to_string(), std::path::PathBuf::from(v)))
|
||||
.collect::<HashMap<_, _>>();
|
||||
let mut compiler = slint_interpreter::ComponentCompiler::default();
|
||||
compiler.set_include_paths(include_paths);
|
||||
compiler.set_library_paths(library_paths);
|
||||
compiler.set_style(String::from("fluent")); // force to fluent style as Qt does not like multi-threaded test execution
|
||||
|
||||
let component =
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@ pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box<dyn Error>>
|
|||
)?;
|
||||
let source = std::fs::read_to_string(&testcase.absolute_path)?;
|
||||
let include_paths = test_driver_lib::extract_include_paths(&source);
|
||||
let library_paths = test_driver_lib::extract_library_paths(&source)
|
||||
.map(|(k, v)| {
|
||||
let mut abs_path = testcase.absolute_path.clone();
|
||||
abs_path.pop();
|
||||
abs_path.push(v);
|
||||
format!("{}={}", k, abs_path.to_string_lossy())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for x in test_driver_lib::extract_test_functions(&source).filter(|x| x.language_id == "js") {
|
||||
write!(main_js, "{{\n {}\n}}\n", x.source.replace("\n", "\n "))?;
|
||||
}
|
||||
|
|
@ -52,6 +60,7 @@ pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box<dyn Error>>
|
|||
.arg(dir.path().join("main.js"))
|
||||
.current_dir(dir.path())
|
||||
.env("SLINT_INCLUDE_PATH", std::env::join_paths(include_paths).unwrap())
|
||||
.env("SLINT_LIBRARY_PATH", std::env::join_paths(library_paths).unwrap())
|
||||
.env("SLINT_SCALE_FACTOR", "1") // We don't have a testing backend, but we can try to force a SF1 as the tests expect.
|
||||
.env("SLINT_ENABLE_EXPERIMENTAL_FEATURES", "1")
|
||||
.stdout(std::process::Stdio::piped())
|
||||
|
|
|
|||
|
|
@ -55,6 +55,14 @@ pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box<dyn Error>>
|
|||
)?;
|
||||
let source = std::fs::read_to_string(&testcase.absolute_path)?;
|
||||
let include_paths = test_driver_lib::extract_include_paths(&source);
|
||||
let library_paths = test_driver_lib::extract_library_paths(&source)
|
||||
.map(|(k, v)| {
|
||||
let mut abs_path = testcase.absolute_path.clone();
|
||||
abs_path.pop();
|
||||
abs_path.push(v);
|
||||
format!("{}={}", k, abs_path.to_string_lossy())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for x in test_driver_lib::extract_test_functions(&source).filter(|x| x.language_id == "js") {
|
||||
write!(main_js, "{{\n {}\n}}\n", x.source.replace("\n", "\n "))?;
|
||||
}
|
||||
|
|
@ -64,6 +72,7 @@ pub fn test(testcase: &test_driver_lib::TestCase) -> Result<(), Box<dyn Error>>
|
|||
.current_dir(dir.path())
|
||||
.env("SLINT_NODE_NATIVE_LIB", std::env::var_os("SLINT_NODE_NATIVE_LIB").unwrap())
|
||||
.env("SLINT_INCLUDE_PATH", std::env::join_paths(include_paths).unwrap())
|
||||
.env("SLINT_LIBRARY_PATH", std::env::join_paths(library_paths).unwrap())
|
||||
.env("SLINT_SCALE_FACTOR", "1") // We don't have a testing backend, but we can try to force a SF1 as the tests expect.
|
||||
.env("SLINT_ENABLE_EXPERIMENTAL_FEATURES", "1")
|
||||
.stdout(std::process::Stdio::piped())
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ fn generate_macro(
|
|||
// to silence all the warnings in .slint files that would be turned into errors
|
||||
output.write_all(b"#![allow(deprecated)]")?;
|
||||
let include_paths = test_driver_lib::extract_include_paths(source);
|
||||
let library_paths = test_driver_lib::extract_library_paths(source);
|
||||
output.write_all(b"slint::slint!{")?;
|
||||
for path in include_paths {
|
||||
let mut abs_path = testcase.absolute_path.clone();
|
||||
|
|
@ -86,6 +87,19 @@ fn generate_macro(
|
|||
|
||||
println!("cargo:rerun-if-changed={}", abs_path.to_string_lossy());
|
||||
}
|
||||
for (lib, path) in library_paths {
|
||||
let mut abs_path = testcase.absolute_path.clone();
|
||||
abs_path.pop();
|
||||
abs_path.push(path);
|
||||
|
||||
output.write_all(b"#[library_path(")?;
|
||||
output.write_all(lib.as_bytes())?;
|
||||
output.write_all(b")=r#\"")?;
|
||||
output.write_all(abs_path.to_string_lossy().as_bytes())?;
|
||||
output.write_all(b"\"#]\n")?;
|
||||
|
||||
println!("cargo:rerun-if-changed={}", abs_path.to_string_lossy());
|
||||
}
|
||||
let mut abs_path = testcase.absolute_path;
|
||||
abs_path.pop();
|
||||
output.write_all(b"#[include_path=r#\"")?;
|
||||
|
|
@ -107,12 +121,16 @@ fn generate_source(
|
|||
let include_paths = test_driver_lib::extract_include_paths(source)
|
||||
.map(std::path::PathBuf::from)
|
||||
.collect::<Vec<_>>();
|
||||
let library_paths = test_driver_lib::extract_library_paths(source)
|
||||
.map(|(k, v)| (k.to_string(), std::path::PathBuf::from(v)))
|
||||
.collect::<std::collections::HashMap<_, _>>();
|
||||
|
||||
let mut diag = BuildDiagnostics::default();
|
||||
let syntax_node = parser::parse(source.to_owned(), Some(&testcase.absolute_path), &mut diag);
|
||||
let mut compiler_config = CompilerConfiguration::new(generator::OutputFormat::Rust);
|
||||
compiler_config.enable_component_containers = true;
|
||||
compiler_config.include_paths = include_paths;
|
||||
compiler_config.library_paths = library_paths;
|
||||
compiler_config.style = Some("fluent".to_string());
|
||||
let (root_component, diag) =
|
||||
spin_on::spin_on(compile_syntax_node(syntax_node, diag, compiler_config));
|
||||
|
|
|
|||
|
|
@ -28,3 +28,4 @@ i-slint-compiler = { workspace = true, features = ["default", "display-diagnosti
|
|||
clap = { version = "4.0", features = ["derive", "wrap_help"] }
|
||||
proc-macro2 = "1.0.11"
|
||||
spin_on = "0.1"
|
||||
itertools = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
use clap::{Parser, ValueEnum};
|
||||
use i_slint_compiler::diagnostics::BuildDiagnostics;
|
||||
use i_slint_compiler::*;
|
||||
use itertools::Itertools;
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||
|
|
@ -32,6 +33,12 @@ struct Cli {
|
|||
#[arg(short = 'I', name = "include path", number_of_values = 1, action)]
|
||||
include_paths: Vec<std::path::PathBuf>,
|
||||
|
||||
/// The argument should be in the format `<library>=<path>` specifying the
|
||||
/// name of the library and the path to the library directory or a .slint
|
||||
/// entry-point file.
|
||||
#[arg(short = 'L', name = "library path", number_of_values = 1, action)]
|
||||
library_paths: Vec<String>,
|
||||
|
||||
/// Path to .slint file ('-' for stdin)
|
||||
#[arg(name = "file", action)]
|
||||
path: std::path::PathBuf,
|
||||
|
|
@ -81,6 +88,11 @@ fn main() -> std::io::Result<()> {
|
|||
}
|
||||
|
||||
compiler_config.include_paths = args.include_paths;
|
||||
compiler_config.library_paths = args
|
||||
.library_paths
|
||||
.iter()
|
||||
.filter_map(|entry| entry.split('=').collect_tuple().map(|(k, v)| (k.into(), v.into())))
|
||||
.collect();
|
||||
if let Some(style) = args.style {
|
||||
compiler_config.style = Some(style);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
|
||||
//! Data structures common between LSP and previewer
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
pub type Error = Box<dyn std::error::Error>;
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
|
@ -15,7 +18,12 @@ pub trait PreviewApi {
|
|||
fn design_mode(&self) -> bool;
|
||||
fn set_contents(&self, path: &Path, contents: &str);
|
||||
fn load_preview(&self, component: PreviewComponent, behavior: PostLoadBehavior);
|
||||
fn config_changed(&self, style: &str, include_paths: &[PathBuf]);
|
||||
fn config_changed(
|
||||
&self,
|
||||
style: &str,
|
||||
include_paths: &[PathBuf],
|
||||
library_paths: &HashMap<String, PathBuf>,
|
||||
);
|
||||
fn highlight(&self, path: Option<PathBuf>, offset: u32) -> Result<()>;
|
||||
}
|
||||
|
||||
|
|
@ -32,6 +40,9 @@ pub struct PreviewComponent {
|
|||
/// The list of include paths
|
||||
pub include_paths: Vec<PathBuf>,
|
||||
|
||||
/// The map of library paths
|
||||
pub library_paths: HashMap<String, PathBuf>,
|
||||
|
||||
/// The style name for the preview
|
||||
pub style: String,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -415,6 +415,7 @@ pub fn show_preview_command(params: &[serde_json::Value], ctx: &Rc<Context>) ->
|
|||
path,
|
||||
component,
|
||||
include_paths: config.include_paths.clone(),
|
||||
library_paths: config.library_paths.clone(),
|
||||
style: config.style.clone().unwrap_or_default(),
|
||||
},
|
||||
crate::common::PostLoadBehavior::ShowAfterLoad,
|
||||
|
|
@ -1206,6 +1207,14 @@ pub async fn load_configuration(ctx: &Context) -> Result<()> {
|
|||
ip.iter().filter_map(|x| x.as_str()).map(PathBuf::from).collect();
|
||||
}
|
||||
}
|
||||
if let Some(lp) = o.get("libraryPaths").and_then(|v| v.as_object()) {
|
||||
if !lp.is_empty() {
|
||||
document_cache.documents.compiler_config.library_paths = lp
|
||||
.iter()
|
||||
.filter_map(|(k, v)| v.as_str().map(|v| (k.to_string(), PathBuf::from(v))))
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
if let Some(style) =
|
||||
o.get("preview").and_then(|v| v.as_object()?.get("style")?.as_str())
|
||||
{
|
||||
|
|
@ -1222,7 +1231,11 @@ pub async fn load_configuration(ctx: &Context) -> Result<()> {
|
|||
|
||||
let cc = &document_cache.documents.compiler_config;
|
||||
let empty_string = String::new();
|
||||
ctx.preview.config_changed(cc.style.as_ref().unwrap_or(&empty_string), &cc.include_paths);
|
||||
ctx.preview.config_changed(
|
||||
cc.style.as_ref().unwrap_or(&empty_string),
|
||||
&cc.include_paths,
|
||||
&cc.library_paths,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,9 +62,14 @@ impl PreviewApi for Previewer {
|
|||
preview::load_preview(self.server_notifier.clone(), _component, _behavior);
|
||||
}
|
||||
|
||||
fn config_changed(&self, _style: &str, _include_paths: &[PathBuf]) {
|
||||
fn config_changed(
|
||||
&self,
|
||||
_style: &str,
|
||||
_include_paths: &[PathBuf],
|
||||
_library_paths: &HashMap<String, PathBuf>,
|
||||
) {
|
||||
#[cfg(feature = "preview")]
|
||||
preview::config_changed(_style, _include_paths);
|
||||
preview::config_changed(_style, _include_paths, _library_paths);
|
||||
}
|
||||
|
||||
fn highlight(&self, _path: Option<std::path::PathBuf>, _offset: u32) -> Result<()> {
|
||||
|
|
|
|||
|
|
@ -147,13 +147,21 @@ pub fn set_contents(path: &Path, content: String) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn config_changed(style: &str, include_paths: &[PathBuf]) {
|
||||
pub fn config_changed(
|
||||
style: &str,
|
||||
include_paths: &[PathBuf],
|
||||
library_paths: &HashMap<String, PathBuf>,
|
||||
) {
|
||||
if let Some(cache) = CONTENT_CACHE.get() {
|
||||
let mut cache = cache.lock().unwrap();
|
||||
let style = style.to_string();
|
||||
if cache.current.style != style || cache.current.include_paths != include_paths {
|
||||
if cache.current.style != style
|
||||
|| cache.current.include_paths != include_paths
|
||||
|| cache.current.library_paths != *library_paths
|
||||
{
|
||||
cache.current.style = style;
|
||||
cache.current.include_paths = include_paths.to_vec();
|
||||
cache.current.library_paths = library_paths.clone();
|
||||
let current = cache.current.clone();
|
||||
let sender = cache.sender.clone();
|
||||
drop(cache);
|
||||
|
|
@ -276,6 +284,7 @@ async fn reload_preview(
|
|||
builder.set_style(preview_component.style);
|
||||
}
|
||||
builder.set_include_paths(preview_component.include_paths);
|
||||
builder.set_library_paths(preview_component.library_paths);
|
||||
|
||||
builder.set_file_loader(|path| {
|
||||
let path = path.to_owned();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use js_sys::Function;
|
|||
pub use language::{Context, DocumentCache, RequestHandler};
|
||||
use serde::Serialize;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::io::ErrorKind;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -69,7 +70,12 @@ impl PreviewApi for Previewer {
|
|||
// do nothing!
|
||||
}
|
||||
|
||||
fn config_changed(&self, _style: &str, _include_paths: &[PathBuf]) {
|
||||
fn config_changed(
|
||||
&self,
|
||||
_style: &str,
|
||||
_include_paths: &[PathBuf],
|
||||
_library_paths: &HashMap<String, PathBuf>,
|
||||
) {
|
||||
// do nothing!
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ serde_json = "1"
|
|||
shlex = "1"
|
||||
spin_on = "0.1"
|
||||
env_logger = "0.10.0"
|
||||
itertools = { workspace = true }
|
||||
|
||||
# Enable image-rs' default features to make all image formats available for preview
|
||||
image = { version = "0.24.0" }
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ slint-viewer path/to/myfile.slint
|
|||
This option is incompatible with `--auto-reload`
|
||||
- `--load-data <file>`: Load the values of public properties from a json file.
|
||||
- `-I <path>`: Add an include path to look for imported .slint files or images.
|
||||
- `-L <library:path>`: Add a library path to look for `@library` imports.
|
||||
- `--style <style>`: Set the style. Defaults to `native` if the Qt backend is compiled, otherwise `fluent`
|
||||
- `--backend <backend>`: Override the Slint rendering backend
|
||||
- `--on <callback> <handler>`: Set a callback handler, see [callback handler](#callback-handlers)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use clap::Parser;
|
||||
use itertools::Itertools;
|
||||
|
||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
|
|
@ -22,6 +23,10 @@ struct Cli {
|
|||
#[arg(short = 'I', name = "include path for other .slint files", number_of_values = 1, action)]
|
||||
include_paths: Vec<std::path::PathBuf>,
|
||||
|
||||
/// The first argument is the library name, and the second argument is the path to the library.
|
||||
#[arg(short = 'L', name = "library path for @library imports", number_of_values = 1, action)]
|
||||
library_paths: Vec<String>,
|
||||
|
||||
/// The .slint file to load ('-' for stdin)
|
||||
#[arg(name = "path to .slint file", action)]
|
||||
path: std::path::PathBuf,
|
||||
|
|
@ -165,6 +170,12 @@ fn init_compiler(
|
|||
compiler.set_translation_domain(domain);
|
||||
}
|
||||
compiler.set_include_paths(args.include_paths.clone());
|
||||
compiler.set_library_paths(
|
||||
args.library_paths
|
||||
.iter()
|
||||
.filter_map(|entry| entry.split('=').collect_tuple().map(|(k, v)| (k.into(), v.into())))
|
||||
.collect(),
|
||||
);
|
||||
if let Some(style) = &args.style {
|
||||
compiler.set_style(style.clone());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue