diff --git a/internal/interpreter/api.rs b/internal/interpreter/api.rs index a017ad968..bcdb37a58 100644 --- a/internal/interpreter/api.rs +++ b/internal/interpreter/api.rs @@ -1221,12 +1221,12 @@ impl ComponentInstance { /// /// WARNING: this is not part of the public API #[cfg(feature = "highlight")] - pub fn element_at_source_code_position( + pub fn element_node_at_source_code_position( &self, path: &Path, offset: u32, - ) -> Vec { - crate::highlight::element_at_source_code_position(&self.inner, path, offset) + ) -> Vec<(i_slint_compiler::object_tree::ElementRc, usize)> { + crate::highlight::element_node_at_source_code_position(&self.inner, path, offset) } } diff --git a/internal/interpreter/highlight.rs b/internal/interpreter/highlight.rs index 4f573318f..d4b158024 100644 --- a/internal/interpreter/highlight.rs +++ b/internal/interpreter/highlight.rs @@ -37,10 +37,11 @@ pub(crate) fn component_positions( generativity::make_guard!(guard); let c = component_instance.unerase(guard); - let elements = find_element_at_offset(&c.description().original, path, offset); + let elements = + find_element_node_at_source_code_position(&c.description().original, path, offset); collect_highlight_data( component_instance, - &elements.into_iter().map(|e| Rc::downgrade(&e)).collect::>(), + &elements.into_iter().map(|(e, _)| Rc::downgrade(&e)).collect::>(), ) } @@ -58,15 +59,15 @@ pub(crate) fn element_positions( values } -pub(crate) fn element_at_source_code_position( +pub(crate) fn element_node_at_source_code_position( component_instance: &DynamicComponentVRc, path: &Path, offset: u32, -) -> Vec { +) -> Vec<(ElementRc, usize)> { generativity::make_guard!(guard); let c = component_instance.unerase(guard); - find_element_at_offset(&c.description().original, path, offset) + find_element_node_at_source_code_position(&c.description().original, path, offset) } fn fill_highlight_data( @@ -113,8 +114,12 @@ fn fill_highlight_data( } // Go over all elements in original to find the one that is highlighted -fn find_element_at_offset(component: &Rc, path: &Path, offset: u32) -> Vec { - let mut result = Vec::::new(); +fn find_element_node_at_source_code_position( + component: &Rc, + path: &Path, + offset: u32, +) -> Vec<(ElementRc, usize)> { + let mut result = Vec::new(); i_slint_compiler::object_tree::recurse_elem_including_sub_components( component, &(), @@ -122,9 +127,15 @@ fn find_element_at_offset(component: &Rc, path: &Path, offset: u32) - if elem.borrow().repeated.is_some() { return; } - for node in elem.borrow().debug.iter().filter_map(|n| n.0.QualifiedName()) { + for (index, node) in elem + .borrow() + .debug + .iter() + .enumerate() + .filter_map(|(i, n)| n.0.QualifiedName().map(|n| (i, n))) + { if node.source_file.path() == path && node.text_range().contains(offset.into()) { - result.push(elem.clone()); + result.push((elem.clone(), index)); } } }, diff --git a/tools/lsp/common.rs b/tools/lsp/common.rs index f34e9f7ae..73e12914d 100644 --- a/tools/lsp/common.rs +++ b/tools/lsp/common.rs @@ -30,6 +30,12 @@ impl std::fmt::Debug for ElementRcNode { } impl ElementRcNode { + pub fn new(element: ElementRc, debug_index: usize) -> Option { + let _ = element.borrow().debug.get(debug_index)?; + + Some(Self { element, debug_index }) + } + pub fn find_in(element: ElementRc, path: &std::path::Path, offset: u32) -> Option { let debug_index = element.borrow().debug.iter().position(|(n, _)| { u32::from(n.text_range().start()) == offset && n.source_file.path() == path diff --git a/tools/lsp/preview.rs b/tools/lsp/preview.rs index 763af46c5..0cfb7a419 100644 --- a/tools/lsp/preview.rs +++ b/tools/lsp/preview.rs @@ -1,7 +1,7 @@ // Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial -use crate::common::{self, ComponentInformation, PreviewComponent, PreviewConfig}; +use crate::common::{self, ComponentInformation, ElementRcNode, PreviewComponent, PreviewConfig}; use crate::lsp_ext::Health; use crate::preview::element_selection::ElementSelection; use crate::util; @@ -423,20 +423,22 @@ pub fn load_preview(preview_component: PreviewComponent) { if notify_editor { if let Some(component_instance) = component_instance() { - if let Some(element) = component_instance - .element_at_source_code_position(&se.path, se.offset) + if let Some((element, debug_index)) = component_instance + .element_node_at_source_code_position(&se.path, se.offset) .first() { - if let Some((node, _)) = - element.borrow().debug.iter().find(|n| !is_element_node_ignored(&n.0)) - { + let Some(element_node) = ElementRcNode::new(element.clone(), *debug_index) + else { + return; + }; + let (path, pos) = element_node.with_element_node(|node| { let sf = &node.source_file; - let pos = util::map_position(sf, se.offset.into()); - ask_editor_to_show_document( - &se.path.to_string_lossy(), - lsp_types::Range::new(pos, pos), - ); - } + (sf.path().to_owned(), util::map_position(sf, se.offset.into())) + }); + ask_editor_to_show_document( + &path.to_string_lossy(), + lsp_types::Range::new(pos, pos), + ); } } } @@ -534,29 +536,22 @@ pub fn highlight(url: Option, offset: u32) { } cache.highlight = highlight; + let selected = selected_element(); + if cache.highlight.as_ref().map_or(true, |(url, _)| cache.dependency.contains(url)) { run_in_ui_thread(move || async move { - let Some(component_instance) = component_instance() else { - return; - }; let Some(path) = url.and_then(|u| Url::to_file_path(&u).ok()) else { return; }; - let elements = component_instance.element_at_source_code_position(&path, offset); - if let Some(e) = elements.first() { - let Some(debug_index) = e.borrow().debug.iter().position(|(n, _)| { - n.text_range().contains(offset.into()) && n.source_file.path() == path - }) else { - return; - }; - let is_layout = - e.borrow().debug.get(debug_index).map_or(false, |(_, l)| l.is_some()); - element_selection::select_element_at_source_code_position( - path, offset, is_layout, None, false, - ); - } else { - element_selection::unselect_element(); + + if Some((path.clone(), offset)) == selected.map(|s| (s.path, s.offset)) { + // Already selected! + return; } + // TODO: false is wrong for is_layout here, but we will replace that soon anyway! + element_selection::select_element_at_source_code_position( + path, offset, false, None, false, + ); }) } } diff --git a/tools/lsp/preview/element_selection.rs b/tools/lsp/preview/element_selection.rs index 3ddf6d001..c8f848765 100644 --- a/tools/lsp/preview/element_selection.rs +++ b/tools/lsp/preview/element_selection.rs @@ -23,8 +23,9 @@ impl ElementSelection { pub fn as_element(&self) -> Option { let component_instance = super::component_instance()?; - let elements = component_instance.element_at_source_code_position(&self.path, self.offset); - elements.get(self.instance_index).or_else(|| elements.first()).cloned() + let elements = + component_instance.element_node_at_source_code_position(&self.path, self.offset); + elements.get(self.instance_index).or_else(|| elements.first()).map(|(e, _)| e.clone()) } pub fn as_element_node(&self) -> Option {