Skip to main content

php_lsp/editing/
use_import.rs

1// Reached only through the server, which is not built for wasm.
2#![cfg_attr(target_family = "wasm", allow(dead_code))]
3
4use std::collections::HashMap;
5use std::sync::Arc;
6
7use tower_lsp_server::ls_types::{Position, Range, TextEdit, Uri, WorkspaceEdit};
8
9use crate::document::ast::ParsedDoc;
10use crate::lang::file_header::use_insert_line;
11use crate::types::resolve::{Declaration, resolve_declaration};
12
13pub(crate) fn find_fqn_for_function(
14    name: &str,
15    get_doc: &dyn Fn(&Uri) -> Option<Arc<ParsedDoc>>,
16    function_candidates: &dyn Fn(&str) -> Vec<Uri>,
17) -> Option<String> {
18    for uri in function_candidates(name) {
19        let Some(doc) = get_doc(&uri) else { continue };
20        let Some(decl) = resolve_declaration(&doc.program().stmts, name, &|decl| {
21            matches!(decl, Declaration::Function { .. })
22        }) else {
23            continue;
24        };
25        if let Declaration::Function { decl, .. } = decl {
26            let ns = doc
27                .program()
28                .stmts
29                .iter()
30                .find_map(|stmt| {
31                    if let php_ast::StmtKind::Namespace(ns) = &stmt.kind {
32                        ns.name.as_ref().map(|n| n.to_string_repr().to_string())
33                    } else {
34                        None
35                    }
36                })
37                .unwrap_or_default();
38            if ns.is_empty() {
39                continue;
40            }
41            return Some(format!(
42                "{}\\{}",
43                ns.trim_start_matches('\\'),
44                decl.name.or_error()
45            ));
46        }
47    }
48    None
49}
50
51/// Build a `WorkspaceEdit` that inserts `use FQN;` into the namespace of the code at `at`.
52pub(crate) fn build_use_import_edit(
53    doc: &ParsedDoc,
54    at: Position,
55    uri: &Uri,
56    fqn: &str,
57) -> WorkspaceEdit {
58    let insert_line = find_use_insert_line(doc, at);
59    let insert_text = format!("use {fqn};\n");
60    let pos = Position {
61        line: insert_line,
62        character: 0,
63    };
64    let edit = TextEdit {
65        range: Range {
66            start: pos,
67            end: pos,
68        },
69        new_text: insert_text,
70    };
71    let mut changes = HashMap::new();
72    changes.insert(uri.clone(), vec![edit]);
73    WorkspaceEdit {
74        changes: Some(changes),
75        ..Default::default()
76    }
77}
78
79/// Build a `WorkspaceEdit` that inserts `use function FQN;` into the namespace of the code at `at`.
80pub(crate) fn build_use_function_import_edit(
81    doc: &ParsedDoc,
82    at: Position,
83    uri: &Uri,
84    fqn: &str,
85) -> WorkspaceEdit {
86    let insert_line = find_use_insert_line(doc, at);
87    let insert_text = format!("use function {fqn};\n");
88    let pos = Position {
89        line: insert_line,
90        character: 0,
91    };
92    let edit = TextEdit {
93        range: Range {
94            start: pos,
95            end: pos,
96        },
97        new_text: insert_text,
98    };
99    let mut changes = HashMap::new();
100    changes.insert(uri.clone(), vec![edit]);
101    WorkspaceEdit {
102        changes: Some(changes),
103        ..Default::default()
104    }
105}
106
107pub(crate) fn find_use_insert_line(doc: &ParsedDoc, at: Position) -> u32 {
108    use_insert_line(doc, at, |_| true)
109}