diff --git a/tools/lsp/common.rs b/tools/lsp/common.rs index 2ca646a824f..72bad51ec1b 100644 --- a/tools/lsp/common.rs +++ b/tools/lsp/common.rs @@ -174,7 +174,7 @@ pub struct ComponentAddition { pub component_type: String, pub import_path: Option, // Url fails to convert reliably:-/ pub insert_position: VersionedPosition, - pub properties: Vec<(String, String)>, + pub component_text: String, } #[allow(unused)] diff --git a/tools/lsp/language.rs b/tools/lsp/language.rs index eac6c7b32a6..32280ad76f0 100644 --- a/tools/lsp/language.rs +++ b/tools/lsp/language.rs @@ -10,15 +10,15 @@ mod goto; mod properties; mod semantic_tokens; #[cfg(test)] -mod test; +pub mod test; use crate::common::{ create_workspace_edit, create_workspace_edit_from_source_file, LspToPreviewMessage, PreviewComponent, PreviewConfig, Result, VersionedUrl, }; -use crate::language::properties::find_element_indent; use crate::util::{ - lookup_current_element_type, map_node, map_position, map_range, map_token, to_lsp_diag, + find_element_indent, lookup_current_element_type, map_node, map_position, map_range, map_token, + to_lsp_diag, }; #[cfg(target_arch = "wasm32")] @@ -1377,28 +1377,20 @@ pub fn add_component( edits.push(edit); } - let new_text = if component.properties.is_empty() { - format!("{} {{ }}\n", component.component_type) - } else { - let mut to_insert = format!("{} {{\n", component.component_type); - for (k, v) in &component.properties { - to_insert += &format!(" {k}: {v};\n"); - } - to_insert += "}\n"; - to_insert - }; - let source_file = doc.node.as_ref().unwrap().source_file.clone(); let ip = map_position(&source_file, component.insert_position.offset().into()); - edits.push(TextEdit { range: lsp_types::Range::new(ip.clone(), ip), new_text }); + edits.push(TextEdit { + range: lsp_types::Range::new(ip.clone(), ip), + new_text: component.component_text, + }); create_workspace_edit_from_source_file(&source_file, edits) .ok_or("Could not create workspace edit".into()) } #[cfg(test)] -mod tests { +pub mod tests { use super::*; use lsp_types::WorkspaceEdit; diff --git a/tools/lsp/language/properties.rs b/tools/lsp/language/properties.rs index 6c4af2de666..2b002dd1902 100644 --- a/tools/lsp/language/properties.rs +++ b/tools/lsp/language/properties.rs @@ -4,8 +4,8 @@ use super::DocumentCache; use crate::common::{create_workspace_edit, Error, Result}; use crate::util::{ - map_node, map_node_and_url, map_position, map_range, to_lsp_diag, with_property_lookup_ctx, - ExpressionContextInfo, + find_element_indent, map_node, map_node_and_url, map_position, map_range, to_lsp_diag, + with_property_lookup_ctx, ExpressionContextInfo, }; use i_slint_compiler::diagnostics::{BuildDiagnostics, SourceFileVersion, Spanned}; @@ -602,20 +602,6 @@ fn set_binding_on_known_property( )) } -// Find the indentation of the element itself as well as the indentation of properties inside the -// element. Returns the element indent followed by the block indent -pub fn find_element_indent(element: &ElementRc) -> Option { - let mut token = - element.borrow().node.first().and_then(|n| n.first_token()).and_then(|t| t.prev_token()); - while let Some(t) = token { - if t.kind() == SyntaxKind::Whitespace && t.text().contains('\n') { - return t.text().split('\n').last().map(|s| s.to_owned()); - } - token = t.prev_token(); - } - None -} - pub(crate) fn set_binding( document_cache: &mut DocumentCache, uri: &lsp_types::Url, @@ -853,27 +839,6 @@ mod tests { assert_eq!(r.end.character, 13); } - #[test] - fn test_find_element_indent() { - let (mut dc, url, _) = loaded_document_cache( - r#"component MainWindow inherits Window { - VerticalBox { - label := Text { text: "text"; } - } -}"# - .to_string(), - ); - - let window = language::element_at_position(&mut dc, &url, &lsp_types::Position::new(0, 30)); - assert_eq!(find_element_indent(&window.unwrap()), None); - - let vbox = language::element_at_position(&mut dc, &url, &lsp_types::Position::new(1, 4)); - assert_eq!(find_element_indent(&vbox.unwrap()), Some(" ".to_string())); - - let label = language::element_at_position(&mut dc, &url, &lsp_types::Position::new(2, 17)); - assert_eq!(find_element_indent(&label.unwrap()), Some(" ".to_string())); - } - fn delete_range_test( content: String, pos_l: u32, diff --git a/tools/lsp/preview/drop_location.rs b/tools/lsp/preview/drop_location.rs index 72e65c5a5a0..2b49b69e81d 100644 --- a/tools/lsp/preview/drop_location.rs +++ b/tools/lsp/preview/drop_location.rs @@ -90,10 +90,26 @@ pub fn drop_at( } }; + let indentation = format!( + "{} ", + crate::util::find_element_indent(&drop_info.target_element).unwrap_or_default() + ); + + let component_text = if properties.is_empty() { + format!("{}{} {{ }}\n", indentation, component_type) + } else { + let mut to_insert = format!("{}{} {{\n", indentation, component_type); + for (k, v) in &properties { + to_insert += &format!("{} {k}: {v};\n", indentation); + } + to_insert += &format!("{}}}\n", indentation); + to_insert + }; + Some(crate::common::ComponentAddition { component_type, + component_text, import_path: if import_path.is_empty() { None } else { Some(import_path) }, insert_position: drop_info.insertion_position, - properties, }) } diff --git a/tools/lsp/util.rs b/tools/lsp/util.rs index 09976656b8d..a64cbdf416a 100644 --- a/tools/lsp/util.rs +++ b/tools/lsp/util.rs @@ -6,7 +6,7 @@ use super::DocumentCache; use i_slint_compiler::diagnostics::{DiagnosticLevel, SourceFile, Spanned}; use i_slint_compiler::langtype::{ElementType, Type}; use i_slint_compiler::lookup::LookupCtx; -use i_slint_compiler::object_tree; +use i_slint_compiler::object_tree::{self, ElementRc}; use i_slint_compiler::parser::{syntax_nodes, SyntaxKind, SyntaxNode, SyntaxToken}; use i_slint_compiler::parser::{TextRange, TextSize}; use i_slint_compiler::typeregister::TypeRegister; @@ -57,6 +57,20 @@ pub fn last_non_ws_token(node: &SyntaxNode) -> Option { last_non_ws } +// Find the indentation of the element itself as well as the indentation of properties inside the +// element. Returns the element indent followed by the block indent +pub fn find_element_indent(element: &ElementRc) -> Option { + let mut token = + element.borrow().node.first().and_then(|n| n.first_token()).and_then(|t| t.prev_token()); + while let Some(t) = token { + if t.kind() == SyntaxKind::Whitespace && t.text().contains('\n') { + return t.text().split('\n').last().map(|s| s.to_owned()); + } + token = t.prev_token(); + } + None +} + /// Given a node within an element, return the Type for the Element under that node. /// (If node is an element, return the Type for that element, otherwise the type of the element under it) /// Will return `Foo` in the following example where `|` is the cursor. @@ -276,3 +290,32 @@ fn to_lsp_diag_level(level: DiagnosticLevel) -> lsp_types::DiagnosticSeverity { _ => lsp_types::DiagnosticSeverity::INFORMATION, } } + +#[cfg(test)] +mod tests { + use super::*; + + use crate::language; + use crate::language::test::loaded_document_cache; + + #[test] + fn test_find_element_indent() { + let (mut dc, url, _) = loaded_document_cache( + r#"component MainWindow inherits Window { + VerticalBox { + label := Text { text: "text"; } + } +}"# + .to_string(), + ); + + let window = language::element_at_position(&mut dc, &url, &lsp_types::Position::new(0, 30)); + assert_eq!(find_element_indent(&window.unwrap()), None); + + let vbox = language::element_at_position(&mut dc, &url, &lsp_types::Position::new(1, 4)); + assert_eq!(find_element_indent(&vbox.unwrap()), Some(" ".to_string())); + + let label = language::element_at_position(&mut dc, &url, &lsp_types::Position::new(2, 17)); + assert_eq!(find_element_indent(&label.unwrap()), Some(" ".to_string())); + } +}