Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

live-preview: Add a button into the Component List to add a new component and to switch over to it #5155

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 11 additions & 32 deletions tools/lsp/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ impl ElementRcNode {
Some(Self { element, debug_index })
}

pub fn find_in(element: ElementRc, path: &std::path::Path, offset: u32) -> Option<Self> {
let debug_index = element.borrow().debug.iter().position(|(n, _)| {
u32::from(n.text_range().start()) == offset && n.source_file.path() == path
})?;

Some(Self { element, debug_index })
}

pub fn find_in_or_below(
element: ElementRc,
path: &std::path::Path,
Expand Down Expand Up @@ -220,23 +212,15 @@ pub fn create_workspace_edit_from_source_files(
#[derive(Clone, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
pub struct VersionedUrl {
/// The file url
url: Url,
pub url: Url,
// The file version
version: UrlVersion,
pub version: UrlVersion,
}

impl VersionedUrl {
pub fn new(url: Url, version: UrlVersion) -> Self {
VersionedUrl { url, version }
}

pub fn url(&self) -> &Url {
&self.url
}

pub fn version(&self) -> &UrlVersion {
&self.version
}
}

impl std::fmt::Debug for VersionedUrl {
Expand All @@ -255,32 +239,27 @@ pub struct Position {
pub offset: u32,
}

#[allow(unused)]
impl Position {
pub fn new(url: Url, offset: u32) -> Self {
Position { url, offset }
}
}

/// A versioned file
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
pub struct VersionedPosition {
/// The file url
url: VersionedUrl,
pub url: VersionedUrl,
/// The offset in the file pointed to by the `url`
offset: u32,
pub offset: u32,
}

#[allow(unused)]
impl VersionedPosition {
pub fn new(url: VersionedUrl, offset: u32) -> Self {
VersionedPosition { url, offset }
}

pub fn url(&self) -> &Url {
self.url.url()
}

pub fn version(&self) -> &UrlVersion {
self.url.version()
}

pub fn offset(&self) -> u32 {
self.offset
}
}

#[derive(Default, Clone, PartialEq, Debug, serde::Deserialize, serde::Serialize)]
Expand Down
3 changes: 2 additions & 1 deletion tools/lsp/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,8 @@ fn get_code_actions(
if has_experimental_client_capability(client_capabilities, "snippetTextEdit") {
let r = util::map_range(&token.source_file, node.parent().unwrap().text_range());
let element = element_at_position(&document_cache.documents, &uri, &r.start);
let element_indent = element.as_ref().and_then(util::find_element_indent);
let element_indent =
element.and_then(|e| e.with_element_node(|n| util::find_node_indent(&n)));
let indented_lines = node
.parent()
.unwrap()
Expand Down
20 changes: 11 additions & 9 deletions tools/lsp/language/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,8 @@ fn create_workspace_edit_for_set_binding_on_known_property(

find_insert_range_for_property(&block_range, properties, property_name).map(
|(range, insert_type)| {
let indent = util::find_element_indent(element).unwrap_or_default();
let indent =
element.with_element_node(|n| util::find_node_indent(&n)).unwrap_or_default();
let edit = lsp_types::TextEdit {
range,
new_text: match insert_type {
Expand Down Expand Up @@ -727,10 +728,10 @@ fn element_at_source_code_position(
dc: &mut language::DocumentCache,
position: &common::VersionedPosition,
) -> Result<common::ElementRcNode> {
let file = lsp_types::Url::to_file_path(position.url())
let file = lsp_types::Url::to_file_path(&position.url.url)
.map_err(|_| "Failed to convert URL to file path".to_string())?;

if &dc.document_version(position.url()) != position.version() {
if dc.document_version(&position.url.url) != position.url.version {
return Err("Document version mismatch.".into());
}

Expand All @@ -741,11 +742,12 @@ fn element_at_source_code_position(
.as_ref()
.map(|n| n.source_file.clone())
.ok_or_else(|| "Document had no node".to_string())?;
let element_position = util::map_position(&source_file, position.offset().into());
let element_position = util::map_position(&source_file, position.offset.into());

Ok(language::element_at_position(&dc.documents, position.url(), &element_position).ok_or_else(
|| format!("No element found at the given start position {:?}", &element_position),
)?)
Ok(language::element_at_position(&dc.documents, &position.url.url, &element_position)
.ok_or_else(|| {
format!("No element found at the given start position {:?}", &element_position)
})?)
}

#[cfg(any(feature = "preview-external", feature = "preview-engine"))]
Expand All @@ -758,8 +760,8 @@ pub fn update_element_properties(

let (_, e) = set_bindings(
&ctx.document_cache.borrow_mut(),
position.url().clone(),
*position.version(),
position.url.url.clone(),
position.url.version,
&element,
&properties,
)?;
Expand Down
116 changes: 113 additions & 3 deletions tools/lsp/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ thread_local! {static PREVIEW_STATE: std::cell::RefCell<PreviewState> = Default:

pub fn set_contents(url: &common::VersionedUrl, content: String) {
let mut cache = CONTENT_CACHE.get_or_init(Default::default).lock().unwrap();
let old = cache.source_code.insert(url.url().clone(), (*url.version(), content.clone()));
if cache.dependency.contains(url.url()) {
let old = cache.source_code.insert(url.url.clone(), (url.version, content.clone()));
if cache.dependency.contains(&url.url) {
if let Some((old_version, old)) = old {
if content == old && old_version == *url.version() {
if content == old && old_version == url.version {
return;
}
}
Expand Down Expand Up @@ -108,6 +108,116 @@ fn search_for_parent_element(root: &ElementRc, child: &ElementRc) -> Option<Elem
None
}

// triggered from the UI, running in UI thread
fn add_component(component_type: slint::SharedString) {
// Find a unique component name
let new_component_type = PREVIEW_STATE.with(|preview_state| {
let nct = component_type.to_string();
let preview_state = preview_state.borrow();

let mut new_component_type = nct.clone();
let mut current_count = 0;

while preview_state
.known_components
.binary_search_by_key(&new_component_type.as_str(), |ci| ci.name.as_str())
.is_ok()
{
current_count += 1;
new_component_type = format!("{nct}{current_count}");
}

new_component_type
});

let (current_url, current_version, current_source) = {
let cache = CONTENT_CACHE.get_or_init(Default::default).lock().unwrap();
let Some(PreviewComponent { url: current_url, .. }) = &cache.current else {
return;
};
let Some((current_version, current_source)) = cache.source_code.get(&current_url).cloned()
else {
return;
};
(current_url.clone(), current_version, current_source)
};

let Ok(current_path) = current_url.to_file_path() else {
return;
};

let Some(component_instance) = component_instance() else {
return;
};

let tl = component_instance.definition().type_loader();
let Some(current_doc) = tl.get_document(&current_path) else {
return;
};

let Some(current_doc_node) = &current_doc.node else {
return;
};

let source_file = current_doc_node.source_file.clone();

let insert_location = current_doc_node
.children()
.find(|child| {
[SyntaxKind::Component, SyntaxKind::ExportsList].contains(&child.kind())
})
.and_then(|c| drop_location::pretty_node_insert_before(&c))
.unwrap_or_else(|| {
let end_offset = current_source.as_bytes().len() as u32;
let pre_indent =
if current_source.ends_with('\n') { "\n".to_string() } else { "\n\n".to_string() };

drop_location::InsertInformation {
insertion_position: common::Position::new(current_url.clone(), end_offset),
replacement_range: 0,
pre_indent,
indent: String::new(),
post_indent: "\n".to_string(),
}
});

let new_text = format!(
"{}component {new_component_type} {{ }}\n\n{}",
insert_location.pre_indent, insert_location.post_indent
);

let start_pos =
util::map_position(&source_file, insert_location.insertion_position.offset.into());
let end_pos = util::map_position(
&source_file,
(insert_location.insertion_position.offset + insert_location.replacement_range).into(),
);
let edit = lsp_types::TextEdit { range: lsp_types::Range::new(start_pos, end_pos), new_text };

let edit = common::create_workspace_edit(current_url.clone(), current_version, vec![edit]);

element_selection::select_element_at_source_code_position(
current_path,
insert_location.insertion_position.offset
+ insert_location.pre_indent.as_bytes().len() as u32,
None,
true,
);

send_message_to_lsp(crate::common::PreviewToLspMessage::SendWorkspaceEdit {
label: Some(format!("Add component {}", new_component_type)),
edit,
});

{
let mut cache = CONTENT_CACHE.get_or_init(Default::default).lock().unwrap();
if let Some(mut preview) = cache.current.take() {
preview.component = Some(new_component_type);
cache.current = Some(preview);
}
}
}

// triggered from the UI, running in UI thread
fn can_drop_component(
component_type: slint::SharedString,
Expand Down