mirror of
https://github.com/slint-ui/slint.git
synced 2025-11-02 04:48:27 +00:00
Port LSP to slint_interpreter::Compiler
Add a feature to only generate a specified component.
This commit is contained in:
parent
bc458ebb26
commit
f93729ffe2
5 changed files with 82 additions and 70 deletions
|
|
@ -54,6 +54,22 @@ pub enum EmbedResourcesKind {
|
|||
EmbedTextures,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ComponentsToGenerate {
|
||||
/// All exported Windows.
|
||||
///
|
||||
/// When set, there will be a warning if an exported component is not a window.
|
||||
AllExportedWindows,
|
||||
/// The Last component (legacy for the viewer / interpreter)
|
||||
|
||||
/// Only the last exported component is generated, regardless if this is a Window or not,
|
||||
/// (and it will be transformed in a Window)
|
||||
LastComponent,
|
||||
|
||||
/// The component with the given name is generated
|
||||
ComponentWithName(String),
|
||||
}
|
||||
|
||||
/// CompilationConfiguration allows configuring different aspects of the compiler.
|
||||
#[derive(Clone)]
|
||||
pub struct CompilerConfiguration {
|
||||
|
|
@ -104,11 +120,7 @@ pub struct CompilerConfiguration {
|
|||
/// Generate debug information for elements (ids, type names)
|
||||
pub debug_info: bool,
|
||||
|
||||
/// When this is true, the passes will generate components for all exported Windows
|
||||
/// and will throw a warning if an exported component is not a window.
|
||||
/// If this is false, only the last component is exported, regardless if this is a Window or not,
|
||||
/// (and it will be transformed in a Window)
|
||||
pub generate_all_exported_windows: bool,
|
||||
pub components_to_generate: ComponentsToGenerate,
|
||||
}
|
||||
|
||||
impl CompilerConfiguration {
|
||||
|
|
@ -157,9 +169,6 @@ impl CompilerConfiguration {
|
|||
|
||||
let debug_info = std::env::var_os("SLINT_EMIT_DEBUG_INFO").is_some();
|
||||
|
||||
// The interpreter currently supports only generating the last component
|
||||
let generate_all_exported_windows = output_format != OutputFormat::Interpreter;
|
||||
|
||||
let cpp_namespace = match output_format {
|
||||
#[cfg(feature = "cpp")]
|
||||
OutputFormat::Cpp(config) => match config.namespace {
|
||||
|
|
@ -186,7 +195,7 @@ impl CompilerConfiguration {
|
|||
translation_domain: None,
|
||||
cpp_namespace,
|
||||
debug_info,
|
||||
generate_all_exported_windows,
|
||||
components_to_generate: ComponentsToGenerate::AllExportedWindows,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ pub fn check_public_api(
|
|||
) {
|
||||
let last = doc.last_exported_component();
|
||||
|
||||
if config.generate_all_exported_windows {
|
||||
doc.exports.retain(|export| {
|
||||
match &config.components_to_generate {
|
||||
crate::ComponentsToGenerate::AllExportedWindows => doc.exports.retain(|export| {
|
||||
// Warn about exported non-window (and remove them from the export unless it's the last for compatibility)
|
||||
if let Either::Left(c) = &export.1 {
|
||||
if !c.is_global() && !super::ensure_window::inherits_window(c) {
|
||||
|
|
@ -32,16 +32,22 @@ pub fn check_public_api(
|
|||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
} else {
|
||||
}),
|
||||
// Only keep the last component if there is one
|
||||
doc.exports.retain(|export| {
|
||||
crate::ComponentsToGenerate::LastComponent => doc.exports.retain(|export| {
|
||||
if let Either::Left(c) = &export.1 {
|
||||
c.is_global() || last.as_ref().map_or(true, |last| Rc::ptr_eq(last, c))
|
||||
} else {
|
||||
true
|
||||
} }),
|
||||
// Only keep the component with the given name
|
||||
crate::ComponentsToGenerate::ComponentWithName(name) => doc.exports.retain(|export| {
|
||||
if let Either::Left(c) = &export.1 {
|
||||
c.is_global() || &c.id == name
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}),
|
||||
}
|
||||
|
||||
for c in doc.exported_roots() {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
//! If there are two carets: ` ^^error{some_regexp}` then it means two line above, and so on with more carets.
|
||||
//! `^warning{regexp}` is also supported.
|
||||
|
||||
use i_slint_compiler::ComponentsToGenerate;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[test]
|
||||
|
|
@ -192,8 +193,13 @@ fn process_file_source(
|
|||
compiler_config.embed_resources = i_slint_compiler::EmbedResourcesKind::OnlyBuiltinResources;
|
||||
compiler_config.enable_experimental = true;
|
||||
compiler_config.style = Some("fluent".into());
|
||||
compiler_config.generate_all_exported_windows =
|
||||
source.contains("config:generate_all_exported_windows");
|
||||
compiler_config.components_to_generate =
|
||||
if source.contains("config:generate_all_exported_windows") {
|
||||
ComponentsToGenerate::AllExportedWindows
|
||||
} else {
|
||||
// Otherwise we'd have lots of warnings about not inheriting Window
|
||||
ComponentsToGenerate::LastComponent
|
||||
};
|
||||
let compile_diagnostics = if !parse_diagnostics.has_error() {
|
||||
let (_, build_diags, _) = spin_on::spin_on(i_slint_compiler::compile_syntax_node(
|
||||
syntax_node.clone(),
|
||||
|
|
|
|||
|
|
@ -503,12 +503,11 @@ pub struct ComponentCompiler {
|
|||
#[allow(deprecated)]
|
||||
impl Default for ComponentCompiler {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
config: i_slint_compiler::CompilerConfiguration::new(
|
||||
i_slint_compiler::generator::OutputFormat::Interpreter,
|
||||
),
|
||||
diagnostics: vec![],
|
||||
}
|
||||
let mut config = i_slint_compiler::CompilerConfiguration::new(
|
||||
i_slint_compiler::generator::OutputFormat::Interpreter,
|
||||
);
|
||||
config.components_to_generate = i_slint_compiler::ComponentsToGenerate::LastComponent;
|
||||
Self { config, diagnostics: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -519,18 +518,6 @@ impl ComponentCompiler {
|
|||
Self::default()
|
||||
}
|
||||
|
||||
/// Allow access to the underlying `CompilerConfiguration`
|
||||
///
|
||||
/// This is an internal function without and ABI or API stability guarantees.
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "internal")]
|
||||
pub fn compiler_configuration(
|
||||
&mut self,
|
||||
_: i_slint_core::InternalToken,
|
||||
) -> &mut i_slint_compiler::CompilerConfiguration {
|
||||
&mut self.config
|
||||
}
|
||||
|
||||
/// Sets the include paths used for looking up `.slint` imports to the specified vector of paths.
|
||||
pub fn set_include_paths(&mut self, include_paths: Vec<std::path::PathBuf>) {
|
||||
self.config.include_paths = include_paths;
|
||||
|
|
@ -710,15 +697,26 @@ pub struct Compiler {
|
|||
|
||||
impl Default for Compiler {
|
||||
fn default() -> Self {
|
||||
let mut config = i_slint_compiler::CompilerConfiguration::new(
|
||||
let config = i_slint_compiler::CompilerConfiguration::new(
|
||||
i_slint_compiler::generator::OutputFormat::Interpreter,
|
||||
);
|
||||
config.generate_all_exported_windows = true;
|
||||
Self { config }
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
/// Allow access to the underlying `CompilerConfiguration`
|
||||
///
|
||||
/// This is an internal function without and ABI or API stability guarantees.
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "internal")]
|
||||
pub fn compiler_configuration(
|
||||
&mut self,
|
||||
_: i_slint_core::InternalToken,
|
||||
) -> &mut i_slint_compiler::CompilerConfiguration {
|
||||
&mut self.config
|
||||
}
|
||||
|
||||
/// Sets the include paths used for looking up `.slint` imports to the specified vector of paths.
|
||||
pub fn set_include_paths(&mut self, include_paths: Vec<std::path::PathBuf>) {
|
||||
self.config.include_paths = include_paths;
|
||||
|
|
|
|||
|
|
@ -789,13 +789,10 @@ fn finish_parsing(ok: bool) {
|
|||
|
||||
if let Some(document_cache) = document_cache() {
|
||||
let mut components = Vec::new();
|
||||
// `_SLINT_LivePreview` gets returned as `-SLINT-LivePreview`, which is unfortunately not a valid identifier.
|
||||
// I do not want to store two constants, so map it over ;-/
|
||||
let private_preview_component = SLINT_LIVE_PREVIEW_COMPONENT.replace('_', "-");
|
||||
component_catalog::builtin_components(&document_cache, &mut components);
|
||||
component_catalog::all_exported_components(
|
||||
&document_cache,
|
||||
&mut |ci| !(ci.is_global || ci.name == private_preview_component),
|
||||
&mut |ci| !ci.is_global,
|
||||
&mut components,
|
||||
);
|
||||
if let Some(previewed_url) = &previewed_url {
|
||||
|
|
@ -997,17 +994,23 @@ async fn parse_source(
|
|||
path: PathBuf,
|
||||
source_code: String,
|
||||
style: String,
|
||||
component: Option<String>,
|
||||
file_loader_fallback: impl Fn(
|
||||
&Path,
|
||||
) -> core::pin::Pin<
|
||||
Box<dyn core::future::Future<Output = Option<std::io::Result<String>>>>,
|
||||
> + 'static,
|
||||
) -> (Vec<diagnostics::Diagnostic>, Option<ComponentDefinition>) {
|
||||
let mut builder = slint_interpreter::ComponentCompiler::default();
|
||||
let mut builder = slint_interpreter::Compiler::default();
|
||||
|
||||
let cc = builder.compiler_configuration(i_slint_core::InternalToken);
|
||||
cc.components_to_generate = if let Some(name) = component {
|
||||
i_slint_compiler::ComponentsToGenerate::ComponentWithName(name)
|
||||
} else {
|
||||
i_slint_compiler::ComponentsToGenerate::LastComponent
|
||||
};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let cc = builder.compiler_configuration(i_slint_core::InternalToken);
|
||||
cc.resource_url_mapper = resource_url_mapper();
|
||||
}
|
||||
|
||||
|
|
@ -1018,42 +1021,31 @@ async fn parse_source(
|
|||
builder.set_library_paths(library_paths);
|
||||
builder.set_file_loader(file_loader_fallback);
|
||||
|
||||
let compiled = builder.build_from_source(source_code, path).await;
|
||||
let result = builder.build_from_source(source_code, path).await;
|
||||
|
||||
(builder.diagnostics().clone(), compiled)
|
||||
let compiled = result.component_names().next().and_then(|name| result.component(name));
|
||||
(result.diagnostics().collect(), compiled)
|
||||
}
|
||||
|
||||
pub const SLINT_LIVE_PREVIEW_COMPONENT: &str = "_SLINT_LivePreview";
|
||||
|
||||
// Must be inside the thread running the slint event loop
|
||||
async fn reload_preview_impl(
|
||||
preview_component: PreviewComponent,
|
||||
style: String,
|
||||
config: PreviewConfig,
|
||||
) {
|
||||
let component = PreviewComponent { style: String::new(), ..preview_component };
|
||||
|
||||
async fn reload_preview_impl(component: PreviewComponent, style: String, config: PreviewConfig) {
|
||||
start_parsing();
|
||||
|
||||
let path = component.url.to_file_path().unwrap_or(PathBuf::from(&component.url.to_string()));
|
||||
let source = {
|
||||
let (_, from_cache) = get_url_from_cache(&component.url).unwrap_or_default();
|
||||
if let Some(component_name) = &component.component {
|
||||
format!(
|
||||
"{from_cache}\nexport component {SLINT_LIVE_PREVIEW_COMPONENT} inherits {component_name} {{ /* {} */ }}\n",
|
||||
common::NODE_IGNORE_COMMENT
|
||||
)
|
||||
} else {
|
||||
from_cache
|
||||
}
|
||||
};
|
||||
|
||||
let (diagnostics, compiled) =
|
||||
parse_source(config.include_paths, config.library_paths, path, source, style, |path| {
|
||||
let (_, source) = get_url_from_cache(&component.url).unwrap_or_default();
|
||||
let (diagnostics, compiled) = parse_source(
|
||||
config.include_paths,
|
||||
config.library_paths,
|
||||
path,
|
||||
source,
|
||||
style,
|
||||
component.component.clone(),
|
||||
|path| {
|
||||
let path = path.to_owned();
|
||||
Box::pin(async move { get_path_from_cache(&path).map(|(_, c)| Result::Ok(c)) })
|
||||
})
|
||||
.await;
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
notify_diagnostics(&diagnostics);
|
||||
|
||||
|
|
@ -1463,6 +1455,7 @@ pub mod test {
|
|||
path,
|
||||
source_code.to_string(),
|
||||
style.to_string(),
|
||||
None,
|
||||
move |path| {
|
||||
let code = code.clone();
|
||||
let path = path.to_owned();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue