Port LSP to slint_interpreter::Compiler

Add a feature to only generate a specified component.
This commit is contained in:
Olivier Goffart 2024-07-01 18:18:31 +02:00
parent bc458ebb26
commit f93729ffe2
5 changed files with 82 additions and 70 deletions

View file

@ -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,
}
}
}

View file

@ -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() {

View file

@ -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(),

View file

@ -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;

View file

@ -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();