Skip to content

Commit 3f019ee

Browse files
committed
Fix cursor relocation for multiline tokens
1 parent 5051705 commit 3f019ee

1 file changed

Lines changed: 86 additions & 13 deletions

File tree

core/src/defaults/reconstructor.rs

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,21 @@ struct CursorTrackerImpl<'cursor> {
3030

3131
enum TokPos {
3232
/// Position of a cursor inside a token
33-
InContent {
33+
Content {
3434
/// Byte offset relative to the start of the token contents (not whitespace)
3535
offset: u32,
3636
},
37+
38+
/// Position of a cursor inside a multiline token
39+
MultilineContent {
40+
/// Byte distance of the cursor from the next line break
41+
reverse_col: u16,
42+
/// The number of line breaks between the cursor and the end of the token content
43+
newlines_after_cursor: u16,
44+
},
45+
3746
/// Position of a cursor in the whitespace before a token
38-
InWhitespace {
47+
Whitespace {
3948
/// Column of the cursor (in bytes), measured from the previous line break
4049
col: u16,
4150
/// The number of line breaks between the cursor and the subsequent token
@@ -118,14 +127,40 @@ impl LogicalLinesReconstructor for DelphiLogicalLinesReconstructor {
118127
let cursors = cursor_positions
119128
.into_iter()
120129
.map(|(tok_pos, cursor, tok_idx)| {
121-
let tok_idx = tok_idx.unwrap_or(tokens.len());
130+
let Some(tok_idx) = tok_idx else {
131+
return InternalCursor {
132+
cursor,
133+
tok_idx: tokens.len(),
134+
tok_pos: TokPos::Content { offset: 0 },
135+
};
136+
};
137+
let tok = &tokens[tok_idx];
122138

123139
let tok_pos = if tok_pos >= 0 {
124-
TokPos::InContent {
125-
offset: tok_pos as u32,
140+
if matches!(
141+
tok.get_token_type(),
142+
RawTokenType::TextLiteral(TextLiteralKind::MultiLine)
143+
| RawTokenType::Comment(CommentKind::MultilineBlock)
144+
) {
145+
let content_after_cursor = &tok.get_content()[tok_pos as usize..];
146+
let reverse_col = content_after_cursor
147+
.split('\n')
148+
.next()
149+
.map(|line| line.len())
150+
.unwrap_or(content_after_cursor.len());
151+
let newlines_after_cursor =
152+
(content_after_cursor.split('\n').count() - 1) as u16;
153+
154+
TokPos::MultilineContent {
155+
reverse_col: reverse_col as u16,
156+
newlines_after_cursor,
157+
}
158+
} else {
159+
TokPos::Content {
160+
offset: tok_pos as u32,
161+
}
126162
}
127163
} else {
128-
let tok = &tokens[tok_idx];
129164
let leading_ws = tok.get_leading_whitespace();
130165
let (ws_before_cursor, ws_after_cursor) =
131166
&leading_ws.split_at(
@@ -140,7 +175,7 @@ impl LogicalLinesReconstructor for DelphiLogicalLinesReconstructor {
140175
+ Self::col_for_token_end_pre_fmt(tokens, tok_idx.wrapping_sub(1))
141176
};
142177

143-
TokPos::InWhitespace {
178+
TokPos::Whitespace {
144179
col: col as u16,
145180
newlines_after_cursor,
146181
}
@@ -253,7 +288,7 @@ impl CursorTracker for CursorTrackerImpl<'_> {
253288
for cursor in &mut self.cursors {
254289
match deleted_token.cmp(&cursor.tok_idx) {
255290
Ordering::Less => cursor.tok_idx -= 1,
256-
Ordering::Equal => cursor.tok_pos = TokPos::InContent { offset: 0 },
291+
Ordering::Equal => cursor.tok_pos = TokPos::Content { offset: 0 },
257292
Ordering::Greater => {}
258293
}
259294
}
@@ -266,7 +301,7 @@ impl CursorTracker for CursorTrackerImpl<'_> {
266301
None => {
267302
// cursor is out of bounds, set it to the last position in the file, if possible
268303
if let Some(t) = formatted_tokens.tokens().next_back() {
269-
cursor.tok_pos = TokPos::InContent {
304+
cursor.tok_pos = TokPos::Content {
270305
offset: t.0.get_content().len() as u32,
271306
};
272307
t
@@ -283,11 +318,25 @@ impl CursorTracker for CursorTrackerImpl<'_> {
283318
.offset_for_token(formatted_tokens, cursor.tok_idx);
284319

285320
cursor.cursor.0 = match cursor.tok_pos {
286-
TokPos::InContent { offset } => {
321+
TokPos::Content { offset } => {
287322
// offset into the token content, but don't go over the end of the token if its length has changed
288323
new_token_offset as u32 + offset.min(tok.get_content().len() as u32)
289324
}
290-
TokPos::InWhitespace {
325+
TokPos::MultilineContent {
326+
reverse_col,
327+
newlines_after_cursor,
328+
} => {
329+
let lines = tok.get_content().rsplit('\n');
330+
let offset_from_end = lines
331+
.take(newlines_after_cursor.into())
332+
// +1 for the separator
333+
.map(|line| line.len() + 1)
334+
.sum::<usize>()
335+
+ reverse_col as usize;
336+
337+
(new_token_offset + tok.get_content().len() - offset_from_end) as u32
338+
}
339+
TokPos::Whitespace {
291340
col,
292341
newlines_after_cursor,
293342
} => {
@@ -703,7 +752,18 @@ A := 1 + '''
703752
''' + | | | | |Spaces;
704753
705754
A := 1 + '''
706-
''' + | | | | | Spaces;",
755+
''' + | | | | | Spaces;
756+
757+
A := '''
758+
|a|aaaa| | |bb| |c|
759+
| | | |a|
760+
|
761+
d|
762+
|''';
763+
764+
A := '''
765+
| d
766+
| |''';",
707767
"\
708768
A
709769
= 1
@@ -725,7 +785,20 @@ A :=
725785
+
726786
'''
727787
'''
728-
+ | | | | |Spaces;",
788+
+ | | | | |Spaces;
789+
790+
A :=
791+
'''
792+
|a|aaaa| | |bb| |c|
793+
| | | |a|
794+
|
795+
d|
796+
|''';
797+
798+
A :=
799+
'''
800+
| d
801+
| |''';",
729802
);
730803
}
731804
}

0 commit comments

Comments
 (0)