Improved scope checking
This commit is contained in:
+66
-13
@@ -23,7 +23,7 @@ static MARKER_COUNTER: AtomicU64 = AtomicU64::new(0);
|
|||||||
const KEYWORDS: [&'static str; 24] = [
|
const KEYWORDS: [&'static str; 24] = [
|
||||||
"if", "else", "for", "while", "match", "return", "break", "continue", "struct", "enum",
|
"if", "else", "for", "while", "match", "return", "break", "continue", "struct", "enum",
|
||||||
"class", "trait", "impl", "pub", "mut", "let", "true", "false", "dyn", "loop", "unsafe",
|
"class", "trait", "impl", "pub", "mut", "let", "true", "false", "dyn", "loop", "unsafe",
|
||||||
"override", "module", "void",
|
"override", "module", "void ",
|
||||||
];
|
];
|
||||||
|
|
||||||
fn keyword_completion_items() -> impl Iterator<Item = CompletionItem> {
|
fn keyword_completion_items() -> impl Iterator<Item = CompletionItem> {
|
||||||
@@ -1004,9 +1004,12 @@ impl LanguageServer for Backend {
|
|||||||
keyword_completion_items().filter(|item| !existing.contains(&item.label)),
|
keyword_completion_items().filter(|item| !existing.contains(&item.label)),
|
||||||
);
|
);
|
||||||
|
|
||||||
if is_at_module_scope(&source, mist_pos.line, mist_pos.character) {
|
if matches!(
|
||||||
|
current_scope(&source, mist_pos.line, mist_pos.character),
|
||||||
|
Scope::Module
|
||||||
|
) {
|
||||||
cleaned.push(CompletionItem {
|
cleaned.push(CompletionItem {
|
||||||
label: "declaration".to_string(),
|
label: "function".to_string(),
|
||||||
kind: Some(CompletionItemKind::SNIPPET),
|
kind: Some(CompletionItemKind::SNIPPET),
|
||||||
insert_text: Some("$1 $2($3)\n{\n$4\n}".to_string()),
|
insert_text: Some("$1 $2($3)\n{\n$4\n}".to_string()),
|
||||||
insert_text_format: Some(InsertTextFormat::SNIPPET),
|
insert_text_format: Some(InsertTextFormat::SNIPPET),
|
||||||
@@ -1035,9 +1038,12 @@ impl LanguageServer for Backend {
|
|||||||
keyword_completion_items().filter(|item| !existing.contains(&item.label)),
|
keyword_completion_items().filter(|item| !existing.contains(&item.label)),
|
||||||
);
|
);
|
||||||
|
|
||||||
if is_at_module_scope(&source, mist_pos.line, mist_pos.character) {
|
if matches!(
|
||||||
|
current_scope(&source, mist_pos.line, mist_pos.character),
|
||||||
|
Scope::Module
|
||||||
|
) {
|
||||||
cleaned.push(CompletionItem {
|
cleaned.push(CompletionItem {
|
||||||
label: "declaration".to_string(),
|
label: "function".to_string(),
|
||||||
kind: Some(CompletionItemKind::SNIPPET),
|
kind: Some(CompletionItemKind::SNIPPET),
|
||||||
insert_text: Some("$1 $2($3)\n{\n$4\n}".to_string()),
|
insert_text: Some("$1 $2($3)\n{\n$4\n}".to_string()),
|
||||||
insert_text_format: Some(InsertTextFormat::SNIPPET),
|
insert_text_format: Some(InsertTextFormat::SNIPPET),
|
||||||
@@ -1577,18 +1583,43 @@ fn ensure_implicit_packages_impl(docs: &mut HashMap<PathBuf, Rope>, src_root: &P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the cursor is at module scope (top-level, not inside a function
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
/// body, class body, or any other brace-delimited block).
|
enum Scope {
|
||||||
fn is_at_module_scope(source: &str, line: u32, character: u32) -> bool {
|
Module,
|
||||||
|
Struct,
|
||||||
|
Enum,
|
||||||
|
Class,
|
||||||
|
Trait,
|
||||||
|
Impl,
|
||||||
|
Block,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scope_for_line(line: &str) -> Scope {
|
||||||
|
for token in line.split_whitespace() {
|
||||||
|
match token {
|
||||||
|
"struct" => return Scope::Struct,
|
||||||
|
"enum" => return Scope::Enum,
|
||||||
|
"class" => return Scope::Class,
|
||||||
|
"trait" => return Scope::Trait,
|
||||||
|
"impl" => return Scope::Impl,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scope::Block
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_scope(source: &str, line: u32, character: u32) -> Scope {
|
||||||
let cursor_offset = match byte_offset_from_lsp(source, line, character) {
|
let cursor_offset = match byte_offset_from_lsp(source, line, character) {
|
||||||
Some(offset) => offset,
|
Some(offset) => offset,
|
||||||
None => return false,
|
None => return Scope::Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut depth: i32 = 0;
|
let mut depth: i32 = 0;
|
||||||
let mut in_string = false;
|
let mut in_string = false;
|
||||||
|
let mut brace_starts: Vec<usize> = Vec::new();
|
||||||
|
|
||||||
for c in source[..cursor_offset].chars() {
|
for (i, c) in source[..cursor_offset].char_indices() {
|
||||||
if in_string {
|
if in_string {
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
in_string = false;
|
in_string = false;
|
||||||
@@ -1597,14 +1628,36 @@ fn is_at_module_scope(source: &str, line: u32, character: u32) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match c {
|
match c {
|
||||||
'{' => depth += 1,
|
'{' => {
|
||||||
'}' if depth > 0 => depth -= 1,
|
depth += 1;
|
||||||
|
brace_starts.push(i);
|
||||||
|
}
|
||||||
|
'}' if depth > 0 => {
|
||||||
|
depth -= 1;
|
||||||
|
brace_starts.pop();
|
||||||
|
}
|
||||||
'"' => in_string = true,
|
'"' => in_string = true,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
depth == 0
|
if depth == 0 {
|
||||||
|
return Scope::Module;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(&brace_pos) = brace_starts.last() else {
|
||||||
|
return Scope::Block;
|
||||||
|
};
|
||||||
|
|
||||||
|
let before = &source[..brace_pos];
|
||||||
|
let before_trimmed = before.trim_end();
|
||||||
|
|
||||||
|
let line_start = before_trimmed[..before_trimmed.len()]
|
||||||
|
.rfind('\n')
|
||||||
|
.map(|pos| pos + 1)
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
scope_for_line(&before_trimmed[line_start..])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_completion_item(mut item: CompletionItem) -> CompletionItem {
|
fn clean_completion_item(mut item: CompletionItem) -> CompletionItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user