use php_ast::{NamespaceBody, Span, Stmt, StmtKind, UseKind};
use tower_lsp_server::ls_types::{Position, Range, TextEdit};
use crate::document::ast::ParsedDoc;
fn find_use_items(source: &str, stmts: &[Stmt<'_, '_>], target: &str, out: &mut Vec<(Span, Span)>) {
for stmt in stmts {
match &stmt.kind {
StmtKind::Use(u) if u.kind == UseKind::Normal => {
for item in u.uses.iter() {
if item.name.to_string_repr().trim_start_matches('\\') != target {
continue;
}
let name_span = item.name.span();
let name_text = &source[name_span.start as usize..name_span.end as usize];
if name_text.contains(['{', '}']) {
continue;
}
out.push((stmt.span, name_span));
}
}
StmtKind::Namespace(ns) => {
if let NamespaceBody::Braced(inner) = &ns.body {
find_use_items(source, &inner.stmts, target, out);
}
}
_ => {}
}
}
}
fn fqn_span_without_backslash(source: &str, span: Span) -> Span {
if source.as_bytes().get(span.start as usize) == Some(&b'\\') {
Span {
start: span.start + 1,
end: span.end,
}
} else {
span
}
}
pub fn delete_use_in_source(doc: &ParsedDoc, fqn: &str) -> Vec<TextEdit> {
let clean = fqn.trim_start_matches('\\');
let sv = doc.view();
let mut matches = Vec::new();
find_use_items(doc.source(), &doc.program().stmts, clean, &mut matches);
let mut seen_lines = std::collections::HashSet::new();
let mut edits = Vec::new();
for (stmt_span, _) in matches {
let line = sv.position_of(stmt_span.start).line;
if !seen_lines.insert(line) {
continue;
}
edits.push(TextEdit {
range: Range {
start: Position { line, character: 0 },
end: Position {
line: line + 1,
character: 0,
},
},
new_text: String::new(),
});
}
edits
}
pub fn use_edits_in_source(doc: &ParsedDoc, old_fqn: &str, new_fqn: &str) -> Vec<TextEdit> {
let old = old_fqn.trim_start_matches('\\');
let new_clean = new_fqn.trim_start_matches('\\');
let source = doc.source();
let sv = doc.view();
let mut matches = Vec::new();
find_use_items(source, &doc.program().stmts, old, &mut matches);
matches
.into_iter()
.map(|(_, name_span)| {
let span = fqn_span_without_backslash(source, name_span);
TextEdit {
range: Range {
start: sv.position_of(span.start),
end: sv.position_of(span.end),
},
new_text: new_clean.to_string(),
}
})
.collect()
}