use std::collections::HashMap;
use std::collections::HashSet;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use parking_lot::{Mutex, RwLock};
use tower_lsp::Client;
pub(crate) type ParseErrorEntry = (String, u32, u32);
pub mod analyse;
pub mod classmap_scanner;
mod code_actions;
mod code_lens;
pub mod completion;
pub mod composer;
pub mod config;
mod definition;
pub mod diagnostics;
pub mod docblock;
mod document_links;
mod document_symbols;
pub mod fix;
mod folding;
mod formatting;
mod highlight;
mod hover;
pub(crate) mod inheritance;
mod inlay_hints;
pub(crate) mod names;
mod parser;
pub(crate) mod phar;
pub mod php_type;
mod phpstan;
mod references;
mod rename;
mod resolution;
pub(crate) mod scope_collector;
mod selection_range;
mod semantic_tokens;
mod server;
mod signature_help;
pub mod stubs;
pub mod subject_expr;
pub(crate) mod subject_extraction;
pub(crate) mod symbol_map;
mod type_hierarchy;
pub mod types;
mod util;
pub(crate) mod virtual_members;
mod workspace_symbols;
#[cfg(test)]
pub mod test_fixtures;
pub use completion::target::extract_completion_target;
pub use types::{AccessKind, ClassInfo, DefineInfo, FunctionInfo, Visibility};
pub use virtual_members::resolve_class_fully;
pub struct Backend {
pub(crate) name: String,
pub(crate) version: String,
pub(crate) client_name: Mutex<String>,
pub(crate) open_files: Arc<RwLock<HashMap<String, Arc<String>>>>,
pub(crate) ast_map: Arc<RwLock<HashMap<String, Vec<Arc<ClassInfo>>>>>,
pub(crate) symbol_maps: Arc<RwLock<HashMap<String, Arc<symbol_map::SymbolMap>>>>,
pub(crate) parse_errors: Arc<RwLock<HashMap<String, Vec<ParseErrorEntry>>>>,
pub(crate) client: Option<Client>,
pub(crate) workspace_root: Arc<RwLock<Option<PathBuf>>>,
pub(crate) psr4_mappings: Arc<RwLock<Vec<composer::Psr4Mapping>>>,
pub(crate) use_map: Arc<RwLock<HashMap<String, HashMap<String, String>>>>,
pub(crate) resolved_names: Arc<RwLock<HashMap<String, Arc<names::OwnedResolvedNames>>>>,
pub(crate) namespace_map: Arc<RwLock<HashMap<String, Option<String>>>>,
pub(crate) global_functions: Arc<RwLock<HashMap<String, (String, FunctionInfo)>>>,
pub(crate) global_defines: Arc<RwLock<HashMap<String, DefineInfo>>>,
pub(crate) autoload_function_index: Arc<RwLock<HashMap<String, PathBuf>>>,
pub(crate) autoload_constant_index: Arc<RwLock<HashMap<String, PathBuf>>>,
pub(crate) autoload_file_paths: Arc<RwLock<Vec<PathBuf>>>,
pub(crate) class_index: Arc<RwLock<HashMap<String, String>>>,
pub(crate) fqn_index: Arc<RwLock<HashMap<String, Arc<ClassInfo>>>>,
pub(crate) class_not_found_cache: Arc<RwLock<HashSet<String>>>,
pub(crate) classmap: Arc<RwLock<HashMap<String, PathBuf>>>,
pub(crate) phar_archives: Arc<RwLock<HashMap<PathBuf, phar::PharArchive>>>,
pub(crate) stub_index: RwLock<HashMap<&'static str, &'static str>>,
pub(crate) resolved_class_cache: virtual_members::ResolvedClassCache,
pub(crate) stub_function_index: RwLock<HashMap<&'static str, &'static str>>,
pub(crate) stub_constant_index: RwLock<HashMap<&'static str, &'static str>>,
pub(crate) php_version: Mutex<types::PhpVersion>,
pub(crate) vendor_uri_prefixes: Mutex<Vec<String>>,
pub(crate) vendor_dir_paths: Mutex<Vec<PathBuf>>,
pub(crate) diag_version: Arc<AtomicU64>,
pub(crate) diag_notify: Arc<tokio::sync::Notify>,
pub(crate) diag_pending_uris: Arc<Mutex<Vec<String>>>,
pub(crate) diag_last_slow: Arc<Mutex<HashMap<String, Vec<tower_lsp::lsp_types::Diagnostic>>>>,
pub(crate) phpstan_notify: Arc<tokio::sync::Notify>,
pub(crate) phpstan_pending_uri: Arc<Mutex<Option<String>>>,
pub(crate) phpstan_last_diags:
Arc<Mutex<HashMap<String, Vec<tower_lsp::lsp_types::Diagnostic>>>>,
pub(crate) diag_result_ids: Arc<Mutex<HashMap<String, u64>>>,
pub(crate) diag_last_full: Arc<Mutex<HashMap<String, Vec<tower_lsp::lsp_types::Diagnostic>>>>,
pub(crate) diag_suppressed: Arc<Mutex<Vec<tower_lsp::lsp_types::Diagnostic>>>,
pub(crate) supports_pull_diagnostics: Arc<std::sync::atomic::AtomicBool>,
pub(crate) supports_file_rename: Arc<std::sync::atomic::AtomicBool>,
pub(crate) supports_work_done_progress: Arc<std::sync::atomic::AtomicBool>,
pub(crate) shutdown_flag: Arc<std::sync::atomic::AtomicBool>,
pub(crate) config: Mutex<config::Config>,
}
impl Backend {
fn defaults() -> Self {
Self {
name: "PHPantom".to_string(),
version: env!("PHPANTOM_GIT_VERSION").to_string(),
client_name: Mutex::new(String::new()),
open_files: Arc::new(RwLock::new(HashMap::new())),
ast_map: Arc::new(RwLock::new(HashMap::new())),
symbol_maps: Arc::new(RwLock::new(HashMap::new())),
parse_errors: Arc::new(RwLock::new(HashMap::new())),
client: None,
workspace_root: Arc::new(RwLock::new(None)),
vendor_uri_prefixes: Mutex::new(Vec::new()),
vendor_dir_paths: Mutex::new(Vec::new()),
psr4_mappings: Arc::new(RwLock::new(Vec::new())),
use_map: Arc::new(RwLock::new(HashMap::new())),
resolved_names: Arc::new(RwLock::new(HashMap::new())),
namespace_map: Arc::new(RwLock::new(HashMap::new())),
global_functions: Arc::new(RwLock::new(HashMap::new())),
global_defines: Arc::new(RwLock::new(HashMap::new())),
autoload_function_index: Arc::new(RwLock::new(HashMap::new())),
autoload_constant_index: Arc::new(RwLock::new(HashMap::new())),
autoload_file_paths: Arc::new(RwLock::new(Vec::new())),
class_index: Arc::new(RwLock::new(HashMap::new())),
fqn_index: Arc::new(RwLock::new(HashMap::new())),
class_not_found_cache: Arc::new(RwLock::new(HashSet::new())),
classmap: Arc::new(RwLock::new(HashMap::new())),
phar_archives: Arc::new(RwLock::new(HashMap::new())),
stub_index: RwLock::new(stubs::build_stub_class_index()),
stub_function_index: RwLock::new(stubs::build_stub_function_index()),
stub_constant_index: RwLock::new(stubs::build_stub_constant_index()),
resolved_class_cache: virtual_members::new_resolved_class_cache(),
php_version: Mutex::new(types::PhpVersion::default()),
diag_version: Arc::new(AtomicU64::new(0)),
diag_notify: Arc::new(tokio::sync::Notify::new()),
diag_pending_uris: Arc::new(Mutex::new(Vec::new())),
diag_last_slow: Arc::new(Mutex::new(HashMap::new())),
phpstan_notify: Arc::new(tokio::sync::Notify::new()),
phpstan_pending_uri: Arc::new(Mutex::new(None)),
phpstan_last_diags: Arc::new(Mutex::new(HashMap::new())),
diag_result_ids: Arc::new(Mutex::new(HashMap::new())),
diag_last_full: Arc::new(Mutex::new(HashMap::new())),
diag_suppressed: Arc::new(Mutex::new(Vec::new())),
supports_pull_diagnostics: Arc::new(std::sync::atomic::AtomicBool::new(false)),
supports_file_rename: Arc::new(std::sync::atomic::AtomicBool::new(false)),
supports_work_done_progress: Arc::new(std::sync::atomic::AtomicBool::new(false)),
shutdown_flag: Arc::new(std::sync::atomic::AtomicBool::new(false)),
config: Mutex::new(config::Config::default()),
}
}
fn test_defaults() -> Self {
Self {
name: "PHPantom".to_string(),
version: env!("PHPANTOM_GIT_VERSION").to_string(),
client_name: Mutex::new(String::new()),
open_files: Arc::new(RwLock::new(HashMap::new())),
ast_map: Arc::new(RwLock::new(HashMap::new())),
symbol_maps: Arc::new(RwLock::new(HashMap::new())),
parse_errors: Arc::new(RwLock::new(HashMap::new())),
client: None,
workspace_root: Arc::new(RwLock::new(None)),
vendor_uri_prefixes: Mutex::new(Vec::new()),
vendor_dir_paths: Mutex::new(Vec::new()),
psr4_mappings: Arc::new(RwLock::new(Vec::new())),
use_map: Arc::new(RwLock::new(HashMap::new())),
resolved_names: Arc::new(RwLock::new(HashMap::new())),
namespace_map: Arc::new(RwLock::new(HashMap::new())),
global_functions: Arc::new(RwLock::new(HashMap::new())),
global_defines: Arc::new(RwLock::new(HashMap::new())),
autoload_function_index: Arc::new(RwLock::new(HashMap::new())),
autoload_constant_index: Arc::new(RwLock::new(HashMap::new())),
autoload_file_paths: Arc::new(RwLock::new(Vec::new())),
class_index: Arc::new(RwLock::new(HashMap::new())),
fqn_index: Arc::new(RwLock::new(HashMap::new())),
class_not_found_cache: Arc::new(RwLock::new(HashSet::new())),
classmap: Arc::new(RwLock::new(HashMap::new())),
phar_archives: Arc::new(RwLock::new(HashMap::new())),
stub_index: RwLock::new(HashMap::new()),
stub_function_index: RwLock::new(HashMap::new()),
stub_constant_index: RwLock::new(HashMap::new()),
resolved_class_cache: virtual_members::new_resolved_class_cache(),
php_version: Mutex::new(types::PhpVersion::default()),
diag_version: Arc::new(AtomicU64::new(0)),
diag_notify: Arc::new(tokio::sync::Notify::new()),
diag_pending_uris: Arc::new(Mutex::new(Vec::new())),
diag_last_slow: Arc::new(Mutex::new(HashMap::new())),
phpstan_notify: Arc::new(tokio::sync::Notify::new()),
phpstan_pending_uri: Arc::new(Mutex::new(None)),
phpstan_last_diags: Arc::new(Mutex::new(HashMap::new())),
diag_result_ids: Arc::new(Mutex::new(HashMap::new())),
diag_last_full: Arc::new(Mutex::new(HashMap::new())),
diag_suppressed: Arc::new(Mutex::new(Vec::new())),
supports_pull_diagnostics: Arc::new(std::sync::atomic::AtomicBool::new(false)),
supports_file_rename: Arc::new(std::sync::atomic::AtomicBool::new(false)),
supports_work_done_progress: Arc::new(std::sync::atomic::AtomicBool::new(false)),
shutdown_flag: Arc::new(std::sync::atomic::AtomicBool::new(false)),
config: Mutex::new(config::Config::default()),
}
}
pub fn new(client: Client) -> Self {
Self {
client: Some(client),
..Self::defaults()
}
}
pub fn new_headless() -> Self {
Self::defaults()
}
pub fn new_test() -> Self {
virtual_members::phpdoc::clear_mixin_cache();
Self::test_defaults()
}
pub fn new_test_with_full_stubs() -> Self {
virtual_members::phpdoc::clear_mixin_cache();
let backend = Self::defaults();
backend.set_php_version(backend.php_version());
backend
}
pub fn new_test_with_stubs(stub_index: HashMap<&'static str, &'static str>) -> Self {
virtual_members::phpdoc::clear_mixin_cache();
let backend = Self {
stub_index: RwLock::new(stub_index),
..Self::test_defaults()
};
backend.set_php_version(backend.php_version());
backend
}
pub fn new_test_with_all_stubs(
stub_index: HashMap<&'static str, &'static str>,
stub_function_index: HashMap<&'static str, &'static str>,
stub_constant_index: HashMap<&'static str, &'static str>,
) -> Self {
virtual_members::phpdoc::clear_mixin_cache();
let backend = Self {
stub_index: RwLock::new(stub_index),
stub_function_index: RwLock::new(stub_function_index),
stub_constant_index: RwLock::new(stub_constant_index),
..Self::test_defaults()
};
backend.set_php_version(backend.php_version());
backend
}
pub fn new_test_with_workspace(
workspace_root: PathBuf,
psr4_mappings: Vec<composer::Psr4Mapping>,
) -> Self {
virtual_members::phpdoc::clear_mixin_cache();
Self {
workspace_root: Arc::new(RwLock::new(Some(workspace_root))),
psr4_mappings: Arc::new(RwLock::new(psr4_mappings)),
..Self::test_defaults()
}
}
pub fn workspace_root(&self) -> &Arc<RwLock<Option<PathBuf>>> {
&self.workspace_root
}
pub fn global_functions(&self) -> &Arc<RwLock<HashMap<String, (String, FunctionInfo)>>> {
&self.global_functions
}
pub fn global_defines(&self) -> &Arc<RwLock<HashMap<String, DefineInfo>>> {
&self.global_defines
}
pub fn class_index(&self) -> &Arc<RwLock<HashMap<String, String>>> {
&self.class_index
}
pub fn psr4_mappings(&self) -> &Arc<RwLock<Vec<composer::Psr4Mapping>>> {
&self.psr4_mappings
}
pub fn classmap(&self) -> &Arc<RwLock<HashMap<String, PathBuf>>> {
&self.classmap
}
pub fn stub_constant_index(
&self,
) -> parking_lot::RwLockReadGuard<'_, HashMap<&'static str, &'static str>> {
self.stub_constant_index.read()
}
pub fn autoload_function_index(&self) -> &Arc<RwLock<HashMap<String, PathBuf>>> {
&self.autoload_function_index
}
pub fn autoload_constant_index(&self) -> &Arc<RwLock<HashMap<String, PathBuf>>> {
&self.autoload_constant_index
}
pub fn autoload_file_paths(&self) -> &Arc<RwLock<Vec<PathBuf>>> {
&self.autoload_file_paths
}
pub fn open_files(&self) -> &Arc<RwLock<HashMap<String, Arc<String>>>> {
&self.open_files
}
pub fn phpstan_last_diags(
&self,
) -> &Arc<Mutex<HashMap<String, Vec<tower_lsp::lsp_types::Diagnostic>>>> {
&self.phpstan_last_diags
}
pub fn php_version(&self) -> types::PhpVersion {
*self.php_version.lock()
}
pub(crate) fn clone_for_diagnostic_worker(&self) -> Self {
Self {
name: self.name.clone(),
version: self.version.clone(),
client_name: Mutex::new(self.client_name.lock().clone()),
open_files: Arc::clone(&self.open_files),
ast_map: Arc::clone(&self.ast_map),
symbol_maps: Arc::clone(&self.symbol_maps),
parse_errors: Arc::clone(&self.parse_errors),
client: self.client.clone(),
workspace_root: Arc::clone(&self.workspace_root),
psr4_mappings: Arc::clone(&self.psr4_mappings),
use_map: Arc::clone(&self.use_map),
resolved_names: Arc::clone(&self.resolved_names),
namespace_map: Arc::clone(&self.namespace_map),
global_functions: Arc::clone(&self.global_functions),
global_defines: Arc::clone(&self.global_defines),
autoload_function_index: Arc::clone(&self.autoload_function_index),
autoload_constant_index: Arc::clone(&self.autoload_constant_index),
autoload_file_paths: Arc::clone(&self.autoload_file_paths),
class_index: Arc::clone(&self.class_index),
fqn_index: Arc::clone(&self.fqn_index),
classmap: Arc::clone(&self.classmap),
phar_archives: Arc::clone(&self.phar_archives),
class_not_found_cache: Arc::clone(&self.class_not_found_cache),
stub_index: RwLock::new(self.stub_index.read().clone()),
resolved_class_cache: Arc::clone(&self.resolved_class_cache),
stub_function_index: RwLock::new(self.stub_function_index.read().clone()),
stub_constant_index: RwLock::new(self.stub_constant_index.read().clone()),
php_version: Mutex::new(self.php_version()),
vendor_uri_prefixes: Mutex::new(self.vendor_uri_prefixes.lock().clone()),
vendor_dir_paths: Mutex::new(self.vendor_dir_paths.lock().clone()),
diag_version: Arc::clone(&self.diag_version),
diag_notify: Arc::clone(&self.diag_notify),
diag_pending_uris: Arc::clone(&self.diag_pending_uris),
diag_last_slow: Arc::clone(&self.diag_last_slow),
phpstan_notify: Arc::clone(&self.phpstan_notify),
phpstan_pending_uri: Arc::clone(&self.phpstan_pending_uri),
phpstan_last_diags: Arc::clone(&self.phpstan_last_diags),
diag_result_ids: Arc::clone(&self.diag_result_ids),
diag_last_full: Arc::clone(&self.diag_last_full),
diag_suppressed: Arc::clone(&self.diag_suppressed),
supports_pull_diagnostics: Arc::clone(&self.supports_pull_diagnostics),
supports_file_rename: Arc::clone(&self.supports_file_rename),
supports_work_done_progress: Arc::clone(&self.supports_work_done_progress),
shutdown_flag: Arc::clone(&self.shutdown_flag),
config: Mutex::new(self.config.lock().clone()),
}
}
pub(crate) fn clone_for_blocking(&self) -> Self {
self.clone_for_diagnostic_worker()
}
pub fn config(&self) -> config::Config {
self.config.lock().clone()
}
pub fn set_config(&self, config: config::Config) {
*self.config.lock() = config;
}
pub fn set_php_version(&self, version: types::PhpVersion) {
*self.php_version.lock() = version;
self.stub_function_index
.write()
.retain(|name, source| !stubs::is_stub_function_removed(source, name, version));
self.stub_index
.write()
.retain(|name, source| !stubs::is_stub_class_removed(source, name, version));
}
}