use super::scope_distance::compute_scope_distance;
use super::{context::CompletionContext, items::CompletionItem};
use perl_semantic_analyzer::symbol::{SymbolKind, SymbolTable};
pub fn add_variable_completions(
completions: &mut Vec<CompletionItem>,
context: &CompletionContext,
kind: SymbolKind,
symbol_table: &SymbolTable,
) {
let sigil = kind.sigil().unwrap_or("");
let prefix_without_sigil = context.prefix.trim_start_matches(sigil);
for (name, symbols) in &symbol_table.symbols {
for symbol in symbols {
if symbol.kind == kind && name.starts_with(prefix_without_sigil) {
let insert_text = format!("{}{}", sigil, name);
let distance =
compute_scope_distance(symbol_table, context.cursor_scope_id, symbol.scope_id);
completions.push(CompletionItem {
label: insert_text.clone(),
kind: crate::completion::items::CompletionItemKind::Variable,
detail: Some(
format!(
"{} {}{}",
symbol.declaration.as_deref().unwrap_or(""),
sigil,
name
)
.trim()
.to_string(),
),
documentation: symbol.documentation.clone(),
insert_text: Some(insert_text),
sort_text: Some(format!("1{}_{}", distance.sort_key(), name)),
filter_text: Some(name.clone()),
additional_edits: vec![],
text_edit_range: Some((context.prefix_start, context.position)),
commit_characters: None,
});
}
}
}
}
pub fn add_special_variables(
completions: &mut Vec<CompletionItem>,
context: &CompletionContext,
sigil: &str,
) {
let special_vars = match sigil {
"$" => vec![
("$_", "Default input and pattern-search space"),
("$.", "Current line number"),
("$,", "Output field separator"),
("$/", "Input record separator"),
("$\\", "Output record separator"),
("$!", "Current errno"),
("$@", "Last eval error"),
("$?", "Child process status (exit code)"),
("$$", "Process ID"),
("$0", "Program name"),
("$1", "First capture group"),
("$&", "Last match"),
("$`", "Prematch"),
("$'", "Postmatch"),
("$+", "Last capture group"),
("$^O", "Operating system name"),
("$^V", "Perl version"),
("$^T", "Script start time (epoch seconds)"),
("$^A", "Accumulator for format() output"),
("$^W", "Warning flag (prefer 'use warnings')"),
],
"@" => vec![
("@_", "Subroutine arguments"),
("@ARGV", "Command line arguments"),
("@INC", "Module search paths"),
("@ISA", "Base classes"),
("@EXPORT", "Exported symbols"),
],
"%" => vec![
("%ENV", "Environment variables"),
("%INC", "Loaded modules"),
("%SIG", "Signal handlers"),
],
_ => vec![],
};
for (var, description) in special_vars {
if var.starts_with(&context.prefix) {
completions.push(CompletionItem {
label: var.to_string(),
kind: crate::completion::items::CompletionItemKind::Variable,
detail: Some("special variable".to_string()),
documentation: Some(description.to_string()),
insert_text: Some(var.to_string()),
sort_text: Some(format!("0_{}", var)), filter_text: Some(var.to_string()),
additional_edits: vec![],
text_edit_range: Some((context.prefix_start, context.position)),
commit_characters: None,
});
}
}
}
pub fn add_all_variables(
completions: &mut Vec<CompletionItem>,
context: &CompletionContext,
symbol_table: &SymbolTable,
) {
if !context.prefix.starts_with(['$', '@', '%', '&']) {
for (name, symbols) in &symbol_table.symbols {
for symbol in symbols {
if symbol.kind.is_variable() && name.starts_with(&context.prefix) {
let sigil = symbol.kind.sigil().unwrap_or("");
let distance = compute_scope_distance(
symbol_table,
context.cursor_scope_id,
symbol.scope_id,
);
completions.push(CompletionItem {
label: format!("{}{}", sigil, name),
kind: crate::completion::items::CompletionItemKind::Variable,
detail: Some(format!(
"{} variable",
symbol.declaration.as_deref().unwrap_or("")
)),
documentation: symbol.documentation.clone(),
insert_text: Some(format!("{}{}", sigil, name)),
sort_text: Some(format!("5{}_{}", distance.sort_key(), name)),
filter_text: Some(name.clone()),
additional_edits: vec![],
text_edit_range: Some((context.prefix_start, context.position)),
commit_characters: None,
});
}
}
}
}
}