Skip to content

Commit 894eb20

Browse files
committed
Fix suggestions at end of file
1 parent 43a697d commit 894eb20

3 files changed

Lines changed: 613 additions & 9 deletions

File tree

src/completion/variable_completion.rs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,22 @@ impl Backend {
4848
/// - `foo|` → `None`
4949
pub fn extract_partial_variable_name(content: &str, position: Position) -> Option<String> {
5050
let lines: Vec<&str> = content.lines().collect();
51-
if position.line as usize >= lines.len() {
51+
if lines.is_empty() {
5252
return None;
5353
}
5454

55-
let line = lines[position.line as usize];
55+
// When the cursor is past the last line (editor can send this for
56+
// a trailing blank line after the final newline), treat it as the
57+
// end of the last line so variables defined earlier are still found.
58+
let (line, col) = if position.line as usize >= lines.len() {
59+
let last = lines[lines.len() - 1];
60+
(last, last.chars().count())
61+
} else {
62+
let l = lines[position.line as usize];
63+
(l, (position.character as usize).min(l.chars().count()))
64+
};
65+
5666
let chars: Vec<char> = line.chars().collect();
57-
let col = (position.character as usize).min(chars.len());
5867

5968
// Walk backwards through identifier characters
6069
let mut i = col;
@@ -181,7 +190,10 @@ impl Backend {
181190
// +1 for the newline character
182191
offset += line.len() + 1;
183192
}
184-
None
193+
// Line number is past the end of the file — treat the cursor as
194+
// being at the very end of the content so that all preceding
195+
// statements are still visible.
196+
Some(content.len())
185197
}
186198
}
187199

@@ -268,6 +280,20 @@ fn find_scope_and_collect<'b>(
268280
}
269281
}
270282

283+
// If the cursor is past the end of the last statement and that
284+
// statement is a namespace, the user is typing at EOF inside an
285+
// unbraced namespace (`namespace Foo;`). The parser's span for the
286+
// namespace may not extend to cover newly-typed content (e.g. a bare
287+
// `$`), so the range check above misses it. Recurse into the last
288+
// namespace so variables declared inside it are still visible.
289+
if let Some(&Statement::Namespace(ns)) = stmts.last() {
290+
let ns_span = ns.span();
291+
if cursor_offset > ns_span.end.offset {
292+
find_scope_and_collect(ns.statements().iter(), cursor_offset, vars);
293+
return;
294+
}
295+
}
296+
271297
// Cursor is in top-level code — collect from all top-level statements.
272298
collect_from_statements(stmts.into_iter(), cursor_offset, vars);
273299
}
@@ -375,8 +401,7 @@ fn collect_from_statements<'b>(
375401
// inside the foreach body. Outside the loop these
376402
// iteration variables should not be in scope.
377403
let body_span = foreach.body.span();
378-
if cursor_offset >= body_span.start.offset
379-
&& cursor_offset <= body_span.end.offset
404+
if cursor_offset >= body_span.start.offset && cursor_offset <= body_span.end.offset
380405
{
381406
if let Some(key_expr) = foreach.target.key() {
382407
collect_var_name_from_expression(key_expr, vars);

src/completion/variable_resolution.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,7 @@ impl Backend {
498498

499499
match &foreach.body {
500500
ForeachBody::Statement(inner) => {
501-
Self::check_statement_for_assignments(
502-
inner, ctx, results, true,
503-
);
501+
Self::check_statement_for_assignments(inner, ctx, results, true);
504502
}
505503
ForeachBody::ColonDelimited(body) => {
506504
Self::walk_statements_for_assignments(

0 commit comments

Comments
 (0)