Skip to content

Commit 95a460a

Browse files
authored
parser: improve error recovery for list items (#684)
1 parent 31fe7a5 commit 95a460a

26 files changed

+1198
-423
lines changed

crates/squawk_ide/src/expand_selection.rs

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,34 @@ use squawk_syntax::{
3333
ast::{self, AstToken},
3434
};
3535

36+
const ALL_LIST_KINDS: &[SyntaxKind] = &[
37+
SyntaxKind::ARG_LIST,
38+
SyntaxKind::ATTRIBUTE_LIST,
39+
SyntaxKind::COLUMN_LIST,
40+
SyntaxKind::CONSTRAINT_EXCLUSION_LIST,
41+
// only separated by whitespace
42+
// SyntaxKind::FUNC_OPTION_LIST,
43+
SyntaxKind::JSON_TABLE_COLUMN_LIST,
44+
SyntaxKind::OPTIONS_LIST,
45+
SyntaxKind::PARAM_LIST,
46+
// only separated by whitespace
47+
// SyntaxKind::SEQUENCE_OPTION_LIST,
48+
SyntaxKind::SET_OPTIONS_LIST,
49+
SyntaxKind::TABLE_ARG_LIST,
50+
SyntaxKind::TABLE_LIST,
51+
SyntaxKind::TARGET_LIST,
52+
SyntaxKind::TRANSACTION_MODE_LIST,
53+
SyntaxKind::VACUUM_OPTION_LIST,
54+
// only separated by whitespace
55+
// SyntaxKind::XML_COLUMN_OPTION_LIST,
56+
SyntaxKind::XML_TABLE_COLUMN_LIST,
57+
];
58+
3659
pub fn extend_selection(root: &SyntaxNode, range: TextRange) -> TextRange {
3760
try_extend_selection(root, range).unwrap_or(range)
3861
}
3962

4063
fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange> {
41-
// TODO: more list_kinds, and add the strings that rust analyzer has
4264
let string_kinds = [
4365
SyntaxKind::COMMENT,
4466
SyntaxKind::STRING,
@@ -48,28 +70,6 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
4870
SyntaxKind::ESC_STRING,
4971
];
5072

51-
// anything that has a separater and whitespace
52-
let list_kinds = [
53-
SyntaxKind::ARG_LIST,
54-
SyntaxKind::ATTRIBUTE_LIST,
55-
SyntaxKind::COLUMN_LIST,
56-
// only separated by whitespace
57-
// SyntaxKind::FUNC_OPTION_LIST,
58-
SyntaxKind::JSON_TABLE_COLUMN_LIST,
59-
SyntaxKind::OPTIONS_LIST,
60-
SyntaxKind::PARAM_LIST,
61-
// only separated by whitespace
62-
// SyntaxKind::SEQUENCE_OPTION_LIST,
63-
SyntaxKind::SET_OPTIONS_LIST,
64-
SyntaxKind::TABLE_ARG_LIST,
65-
SyntaxKind::TABLE_LIST,
66-
SyntaxKind::TARGET_LIST,
67-
SyntaxKind::TRANSACTION_MODE_LIST,
68-
// only separated by whitespace
69-
// SyntaxKind::XML_COLUMN_OPTION_LIST,
70-
SyntaxKind::XML_TABLE_COLUMN_LIST,
71-
];
72-
7373
if range.is_empty() {
7474
let offset = range.start();
7575
let mut leaves = root.token_at_offset(offset);
@@ -116,7 +116,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
116116

117117
if node
118118
.parent()
119-
.is_some_and(|n| list_kinds.contains(&n.kind()))
119+
.is_some_and(|n| ALL_LIST_KINDS.contains(&n.kind()))
120120
{
121121
if let Some(range) = extend_list_item(&node) {
122122
return Some(range);
@@ -540,4 +540,46 @@ $0
540540
]
541541
"#);
542542
}
543+
544+
#[test]
545+
fn list_variants() {
546+
// only separated by whitespace
547+
const EXCLUDED_LIST_KINDS: &[SyntaxKind] = &[
548+
SyntaxKind::FUNC_OPTION_LIST,
549+
SyntaxKind::SEQUENCE_OPTION_LIST,
550+
SyntaxKind::XML_COLUMN_OPTION_LIST,
551+
];
552+
553+
let generated_list_kinds: Vec<SyntaxKind> = (0..SyntaxKind::__LAST as u16)
554+
.map(SyntaxKind::from)
555+
.filter(|kind| {
556+
format!("{:?}", kind).ends_with("_LIST") && !EXCLUDED_LIST_KINDS.contains(kind)
557+
})
558+
.collect();
559+
560+
assert_debug_snapshot!(generated_list_kinds, @r"
561+
[
562+
ARG_LIST,
563+
ATTRIBUTE_LIST,
564+
COLUMN_LIST,
565+
CONSTRAINT_EXCLUSION_LIST,
566+
JSON_TABLE_COLUMN_LIST,
567+
OPTIONS_LIST,
568+
PARAM_LIST,
569+
SET_OPTIONS_LIST,
570+
TABLE_ARG_LIST,
571+
TABLE_LIST,
572+
TARGET_LIST,
573+
TRANSACTION_MODE_LIST,
574+
VACUUM_OPTION_LIST,
575+
XML_TABLE_COLUMN_LIST,
576+
]
577+
");
578+
579+
assert_eq!(
580+
ALL_LIST_KINDS,
581+
generated_list_kinds.as_slice(),
582+
"ALL_LIST_KINDS constant is out of sync with actual _LIST variants"
583+
);
584+
}
543585
}

crates/squawk_parser/src/generated/syntax_kind.rs

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)