Skip to content

Commit

Permalink
live preview: Indent inserted elements (better)
Browse files Browse the repository at this point in the history
  • Loading branch information
hunger committed Feb 13, 2024
1 parent 5d13878 commit 67c6f1b
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 56 deletions.
2 changes: 1 addition & 1 deletion tools/lsp/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ pub struct ComponentAddition {
pub component_type: String,
pub import_path: Option<String>, // Url fails to convert reliably:-/
pub insert_position: VersionedPosition,
pub properties: Vec<(String, String)>,
pub component_text: String,
}

#[allow(unused)]
Expand Down
24 changes: 8 additions & 16 deletions tools/lsp/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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;
Expand Down
39 changes: 2 additions & 37 deletions tools/lsp/language/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<String> {
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,
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 17 additions & 1 deletion tools/lsp/preview/drop_location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
}
45 changes: 44 additions & 1 deletion tools/lsp/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,6 +57,20 @@ pub fn last_non_ws_token(node: &SyntaxNode) -> Option<SyntaxToken> {
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<String> {
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.
Expand Down Expand Up @@ -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()));
}
}

0 comments on commit 67c6f1b

Please sign in to comment.