Fix LSP semantic coloration bailing out sometimes

Step to reproduce:

```
xx := Window {
   TouchArea {
      clicked => {
         if (true) {}
         // note: because there is no else, the semantic colouring is bailing out now
      }
   }
   TouchArea { clicked => {} }
}
```

This is caused by a bug in rowan::SyntaxToken::next_token that doesn't visit
the next token if it has an empty node

Unfortunately, the LSP has no tests, so I couldn't add one easily
This commit is contained in:
Olivier Goffart 2022-02-04 13:21:26 +01:00
parent 458c88cfa4
commit c5ef368a3f
2 changed files with 36 additions and 6 deletions

View file

@ -693,6 +693,31 @@ impl SyntaxToken {
pub fn parent(&self) -> SyntaxNode { pub fn parent(&self) -> SyntaxNode {
SyntaxNode { node: self.token.parent().unwrap(), source_file: self.source_file.clone() } SyntaxNode { node: self.token.parent().unwrap(), source_file: self.source_file.clone() }
} }
pub fn next_token(&self) -> Option<SyntaxToken> {
// Due to a bug (as of rowan 0.15.3), rowan::SyntaxToken::next_token doesn't work if a
// sibling don't have tokens.
// For example, if we have an expression like `if (true) {}` the
// ConditionalExpression has an empty Expression/CodeBlock for the else part,
// and next_token doesn't go into that.
// So re-implement
let token = self
.token
.next_sibling_or_token()
.and_then(|e| match e {
rowan::NodeOrToken::Node(n) => n.first_token(),
rowan::NodeOrToken::Token(t) => Some(t),
})
.or_else(|| {
self.token.ancestors().find_map(|it| it.next_sibling_or_token()).and_then(|e| {
match e {
rowan::NodeOrToken::Node(n) => n.first_token(),
rowan::NodeOrToken::Token(t) => Some(t),
}
})
})?;
Some(SyntaxToken { token, source_file: self.source_file.clone() })
}
} }
impl std::fmt::Display for SyntaxToken { impl std::fmt::Display for SyntaxToken {
@ -745,6 +770,11 @@ impl SyntaxNode {
pub fn parent(&self) -> Option<SyntaxNode> { pub fn parent(&self) -> Option<SyntaxNode> {
self.node.parent().map(|node| SyntaxNode { node, source_file: self.source_file.clone() }) self.node.parent().map(|node| SyntaxNode { node, source_file: self.source_file.clone() })
} }
pub fn first_token(&self) -> Option<SyntaxToken> {
self.node
.first_token()
.map(|token| SyntaxToken { token, source_file: self.source_file.clone() })
}
} }
#[derive(Debug, Clone, derive_more::From)] #[derive(Debug, Clone, derive_more::From)]

View file

@ -44,7 +44,7 @@ pub fn get_semantic_tokens(
SyntaxKind::StringLiteral => Some((self::STRING, 0)), SyntaxKind::StringLiteral => Some((self::STRING, 0)),
SyntaxKind::NumberLiteral => Some((self::NUMBER, 0)), SyntaxKind::NumberLiteral => Some((self::NUMBER, 0)),
SyntaxKind::ColorLiteral => Some((self::NUMBER, 0)), SyntaxKind::ColorLiteral => Some((self::NUMBER, 0)),
SyntaxKind::Identifier => match token.parent()?.kind() { SyntaxKind::Identifier => match token.parent().kind() {
SyntaxKind::Component => Some((self::KEYWORD, 0)), SyntaxKind::Component => Some((self::KEYWORD, 0)),
// the id of the element // the id of the element
SyntaxKind::SubElement => Some((self::VARIABLE, 1 << self::DEFINITION)), SyntaxKind::SubElement => Some((self::VARIABLE, 1 << self::DEFINITION)),
@ -55,7 +55,7 @@ pub fn get_semantic_tokens(
SyntaxKind::CallbackConnection => Some((self::FUNCTION, 0)), SyntaxKind::CallbackConnection => Some((self::FUNCTION, 0)),
SyntaxKind::PropertyDeclaration => Some((self::KEYWORD, 0)), SyntaxKind::PropertyDeclaration => Some((self::KEYWORD, 0)),
SyntaxKind::PropertyAnimation => Some((self::KEYWORD, 0)), SyntaxKind::PropertyAnimation => Some((self::KEYWORD, 0)),
SyntaxKind::QualifiedName => match token.parent()?.parent()?.kind() { SyntaxKind::QualifiedName => match token.parent().parent()?.kind() {
SyntaxKind::Type => Some((self::TYPE, 0)), SyntaxKind::Type => Some((self::TYPE, 0)),
// the base type // the base type
SyntaxKind::Element => Some((self::TYPE, 0)), SyntaxKind::Element => Some((self::TYPE, 0)),
@ -66,7 +66,7 @@ pub fn get_semantic_tokens(
_ => None, _ => None,
}, },
SyntaxKind::DeclaredIdentifier => { SyntaxKind::DeclaredIdentifier => {
match token.parent()?.parent()?.kind() { match token.parent().parent()?.kind() {
SyntaxKind::Component => Some((self::TYPE, 1 << self::DEFINITION)), SyntaxKind::Component => Some((self::TYPE, 1 << self::DEFINITION)),
SyntaxKind::RepeatedElement => { SyntaxKind::RepeatedElement => {
Some((self::PROPERTY, 1 << self::DEFINITION)) Some((self::PROPERTY, 1 << self::DEFINITION))
@ -104,7 +104,7 @@ pub fn get_semantic_tokens(
SyntaxKind::ExportIdentifier => { SyntaxKind::ExportIdentifier => {
Some(( Some((
self::TYPE, self::TYPE,
if token.parent()?.parent().map_or(false, |p| { if token.parent().parent().map_or(false, |p| {
p.children().any(|n| n.kind() == SyntaxKind::ExportName) p.children().any(|n| n.kind() == SyntaxKind::ExportName)
}) { }) {
0 0
@ -118,7 +118,7 @@ pub fn get_semantic_tokens(
SyntaxKind::ImportIdentifier => Some((self::KEYWORD, 0)), SyntaxKind::ImportIdentifier => Some((self::KEYWORD, 0)),
SyntaxKind::ExternalName => Some(( SyntaxKind::ExternalName => Some((
self::TYPE, self::TYPE,
if token.parent()?.parent().map_or(false, |p| { if token.parent().parent().map_or(false, |p| {
p.children().any(|n| n.kind() == SyntaxKind::InternalName) p.children().any(|n| n.kind() == SyntaxKind::InternalName)
}) { }) {
0 0
@ -141,7 +141,7 @@ pub fn get_semantic_tokens(
| SyntaxKind::NotEqual | SyntaxKind::NotEqual
| SyntaxKind::OrOr | SyntaxKind::OrOr
| SyntaxKind::AndAnd => Some((self::OPERATOR, 0)), | SyntaxKind::AndAnd => Some((self::OPERATOR, 0)),
SyntaxKind::LAngle | SyntaxKind::RAngle => (token.parent()?.kind() SyntaxKind::LAngle | SyntaxKind::RAngle => (token.parent().kind()
== SyntaxKind::PropertyDeclaration) == SyntaxKind::PropertyDeclaration)
.then(|| (self::OPERATOR, 0)), .then(|| (self::OPERATOR, 0)),
SyntaxKind::Plus SyntaxKind::Plus