Skip to content

Commit be2b653

Browse files
committed
lsp: fix removing binding of declared property
When we have a property such as `property <int> foo: 45;` And the live-preview user wants to reset the value of that property, we should keep the property there. So the result should be `property <int> foo;` and not remove the property completely as the old code was doing, otherwise it is likely that this wouldn't compile if the property was used anyway.
1 parent 499e32e commit be2b653

File tree

1 file changed

+175
-54
lines changed

1 file changed

+175
-54
lines changed

tools/lsp/preview/properties.rs

Lines changed: 175 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -831,65 +831,79 @@ pub fn remove_binding(
831831
) -> Result<lsp_types::WorkspaceEdit> {
832832
let source_file = element.with_element_node(|node| node.source_file.clone());
833833

834-
let range = find_property_binding_offset(element, property_name)
834+
let token = find_property_binding_offset(element, property_name)
835835
.and_then(|offset| {
836836
element.with_element_node(|node| node.token_at_offset(offset.into()).right_biased())
837837
})
838-
.and_then(|token| {
839-
for ancestor in token.parent_ancestors() {
840-
if (ancestor.kind() == SyntaxKind::Binding)
841-
|| (ancestor.kind() == SyntaxKind::PropertyDeclaration)
842-
{
843-
let start = {
844-
let token = left_extend(ancestor.first_token()?);
845-
let start = token.text_range().start();
846-
token
847-
.prev_token()
848-
.and_then(|t| {
849-
if t.kind() == SyntaxKind::Whitespace && t.text().contains('\n') {
850-
let to_sub =
851-
t.text().split('\n').next_back().unwrap_or_default().len()
852-
as u32;
853-
start.checked_sub(to_sub.into())
854-
} else {
855-
None
856-
}
857-
})
858-
.unwrap_or(start)
859-
};
860-
let end = {
861-
let token = right_extend(ancestor.last_token()?);
862-
let end = token.text_range().end();
863-
token
864-
.next_token()
865-
.and_then(|t| {
866-
if t.kind() == SyntaxKind::Whitespace && t.text().contains('\n') {
867-
let to_add =
868-
t.text().split('\n').next().unwrap_or_default().len()
869-
as u32;
870-
end.checked_add((to_add + 1/* <cr> */).into())
871-
} else {
872-
None
873-
}
874-
})
875-
.unwrap_or(end)
876-
};
877-
878-
return Some(util::text_range_to_lsp_range(
879-
&source_file,
880-
TextRange::new(start, end),
881-
));
882-
}
883-
if ancestor.kind() == SyntaxKind::Element {
884-
// There should have been a binding before the element!
885-
break;
886-
}
838+
.ok_or("Could not find property to delete.")?;
839+
840+
for ancestor in token.parent_ancestors() {
841+
if ancestor.kind() == SyntaxKind::PropertyDeclaration {
842+
let prop_decl = syntax_nodes::PropertyDeclaration::from(ancestor.clone());
843+
let binding =
844+
prop_decl.BindingExpression().ok_or("property declaration has no binding")?;
845+
let colon = ancestor
846+
.child_token(SyntaxKind::Colon)
847+
.ok_or("property peclaration has no colon")?;
848+
let start = colon.text_range().start();
849+
if let Some(semi_colon) = binding.child_token(SyntaxKind::Semicolon) {
850+
let end = semi_colon.text_range().start();
851+
let range = util::text_range_to_lsp_range(&source_file, TextRange::new(start, end));
852+
let edit = lsp_types::TextEdit { range, new_text: String::new() };
853+
return Ok(common::create_workspace_edit(uri.clone(), version, vec![edit]));
854+
} else if let Some(closing_brace) =
855+
binding.CodeBlock().and_then(|cb| cb.child_token(SyntaxKind::RBrace))
856+
{
857+
let end = closing_brace.text_range().end();
858+
let range = util::text_range_to_lsp_range(&source_file, TextRange::new(start, end));
859+
let edit = lsp_types::TextEdit { range, new_text: ";".into() };
860+
return Ok(common::create_workspace_edit(uri.clone(), version, vec![edit]));
861+
} else {
862+
return Err("Could not find end of range to delete.".into());
887863
}
888-
None
889-
})
890-
.ok_or_else(|| Into::<common::Error>::into("Could not find range to delete."))?;
864+
} else if ancestor.kind() == SyntaxKind::Binding {
865+
let start = {
866+
let token = left_extend(ancestor.first_token().ok_or("empty binding")?);
867+
let start = token.text_range().start();
868+
token
869+
.prev_token()
870+
.and_then(|t| {
871+
if t.kind() == SyntaxKind::Whitespace && t.text().contains('\n') {
872+
let to_sub =
873+
t.text().split('\n').next_back().unwrap_or_default().len() as u32;
874+
start.checked_sub(to_sub.into())
875+
} else {
876+
None
877+
}
878+
})
879+
.unwrap_or(start)
880+
};
881+
let end = {
882+
let token = right_extend(ancestor.last_token().ok_or("empty binding")?);
883+
let end = token.text_range().end();
884+
token
885+
.next_token()
886+
.and_then(|t| {
887+
if t.kind() == SyntaxKind::Whitespace && t.text().contains('\n') {
888+
let to_add =
889+
t.text().split('\n').next().unwrap_or_default().len() as u32;
890+
end.checked_add((to_add + 1/* <cr> */).into())
891+
} else {
892+
None
893+
}
894+
})
895+
.unwrap_or(end)
896+
};
891897

892-
Ok(create_workspace_edit_for_remove_binding(uri, version, range))
898+
let range = util::text_range_to_lsp_range(&source_file, TextRange::new(start, end));
899+
return Ok(create_workspace_edit_for_remove_binding(uri, version, range));
900+
}
901+
if ancestor.kind() == SyntaxKind::Element {
902+
// There should have been a binding before the element!
903+
break;
904+
}
905+
}
906+
Err("Could not find range to delete.".into())
893907
}
894908

895909
#[cfg(test)]
@@ -1915,4 +1929,111 @@ component MyComp {
19151929
assert_eq!(tc.range.start, lsp_types::Position { line: 17, character: 27 });
19161930
assert_eq!(tc.range.end, lsp_types::Position { line: 17, character: 32 });
19171931
}
1932+
1933+
#[test]
1934+
fn test_remove_binding() {
1935+
let source = r#"
1936+
component Foo inherits Window {
1937+
width: 30px;
1938+
background: red;
1939+
Foo { background: blue; }
1940+
}
1941+
"#;
1942+
let (dc, uri, _) = crate::language::test::loaded_document_cache(source.into());
1943+
let elem = dc
1944+
.element_at_offset(&uri, TextSize::new(source.find("Window").unwrap() as u32))
1945+
.unwrap();
1946+
let edit = remove_binding(uri.clone(), None, &elem, "background").unwrap();
1947+
1948+
let applied = crate::common::text_edit::apply_workspace_edit(&dc, &edit).unwrap();
1949+
assert_eq!(
1950+
applied.first().unwrap().contents,
1951+
r#"
1952+
component Foo inherits Window {
1953+
width: 30px;
1954+
Foo { background: blue; }
1955+
}
1956+
"#
1957+
);
1958+
1959+
let elem = dc
1960+
.element_at_offset(&uri, TextSize::new(source.find("Foo {").unwrap() as u32))
1961+
.unwrap();
1962+
let edit = remove_binding(uri.clone(), None, &elem, "background").unwrap();
1963+
1964+
let applied = crate::common::text_edit::apply_workspace_edit(&dc, &edit).unwrap();
1965+
assert_eq!(
1966+
applied.first().unwrap().contents,
1967+
r#"
1968+
component Foo inherits Window {
1969+
width: 30px;
1970+
background: red;
1971+
Foo { }
1972+
}
1973+
"#
1974+
);
1975+
}
1976+
1977+
#[test]
1978+
fn test_remove_binding_in_declaration() {
1979+
let source = r#"
1980+
component Foo inherits Window {
1981+
property <int> test1: 45 + 78; // comment
1982+
property <int> test2: {
1983+
return 4;
1984+
}
1985+
property <{x: int}> test3: { return { x: 0 }; };
1986+
background: violet;
1987+
}
1988+
"#;
1989+
let (dc, uri, _) = crate::language::test::loaded_document_cache(source.into());
1990+
let elem = dc
1991+
.element_at_offset(&uri, TextSize::new(source.find("Window").unwrap() as u32))
1992+
.unwrap();
1993+
let edit = remove_binding(uri.clone(), None, &elem, "test1").unwrap();
1994+
let applied = crate::common::text_edit::apply_workspace_edit(&dc, &edit).unwrap();
1995+
assert_eq!(
1996+
applied.first().unwrap().contents,
1997+
r#"
1998+
component Foo inherits Window {
1999+
property <int> test1; // comment
2000+
property <int> test2: {
2001+
return 4;
2002+
}
2003+
property <{x: int}> test3: { return { x: 0 }; };
2004+
background: violet;
2005+
}
2006+
"#
2007+
);
2008+
2009+
let edit = remove_binding(uri.clone(), None, &elem, "test2").unwrap();
2010+
let applied = crate::common::text_edit::apply_workspace_edit(&dc, &edit).unwrap();
2011+
assert_eq!(
2012+
applied.first().unwrap().contents,
2013+
r#"
2014+
component Foo inherits Window {
2015+
property <int> test1: 45 + 78; // comment
2016+
property <int> test2;
2017+
property <{x: int}> test3: { return { x: 0 }; };
2018+
background: violet;
2019+
}
2020+
"#
2021+
);
2022+
2023+
let edit = remove_binding(uri.clone(), None, &elem, "test3").unwrap();
2024+
let applied = crate::common::text_edit::apply_workspace_edit(&dc, &edit).unwrap();
2025+
assert_eq!(
2026+
applied.first().unwrap().contents,
2027+
r#"
2028+
component Foo inherits Window {
2029+
property <int> test1: 45 + 78; // comment
2030+
property <int> test2: {
2031+
return 4;
2032+
}
2033+
property <{x: int}> test3;
2034+
background: violet;
2035+
}
2036+
"#
2037+
);
2038+
}
19182039
}

0 commit comments

Comments
 (0)