Skip to content

Commit

Permalink
Allow specifying paths for @library imports
Browse files Browse the repository at this point in the history
  • Loading branch information
jpnurmi committed Oct 20, 2023
1 parent 23a9f5a commit 4f916bc
Show file tree
Hide file tree
Showing 32 changed files with 485 additions and 56 deletions.
1 change: 1 addition & 0 deletions api/napi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ i-slint-core = { workspace = true, features = ["default"] }
slint-interpreter = { workspace = true, features = ["default", "display-diagnostics"] }
spin_on = "0.1"
css-color-parser2 = { workspace = true }
itertools = { workspace = true }

[build-dependencies]
napi-build = "2.0.1"
15 changes: 15 additions & 0 deletions api/napi/src/interpreter/component_compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }
}

Expand Down
1 change: 1 addition & 0 deletions api/node/native/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
15 changes: 15 additions & 0 deletions api/node/native/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down
34 changes: 34 additions & 0 deletions api/rs/build/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
71 changes: 50 additions & 21 deletions api/rs/macros/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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 {
Expand All @@ -270,32 +286,44 @@ 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,
}
}
_ => 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
Expand All @@ -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);
Expand All @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions editors/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
},
Expand Down
4 changes: 4 additions & 0 deletions internal/compiler/lib.rs
Original file line number Diff line number Diff line change
Expand 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;
Expand Down Expand Up @@ -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>,

Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions internal/compiler/tests/typeloader/dependency_test_main.slint
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
}
Original file line number Diff line number Diff line change
@@ -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 changes: 5 additions & 0 deletions internal/compiler/tests/typeloader/library/lib.slint
Original file line number Diff line number Diff line change
@@ -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 }
Original file line number Diff line number Diff line change
@@ -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 {}
2 changes: 2 additions & 0 deletions internal/compiler/tests/typeloader/some_rust_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
}
);

0 comments on commit 4f916bc

Please sign in to comment.