#[cfg(feature = "semantic")]
use std::cell::Cell;
#[cfg(feature = "semantic")]
use std::collections::BTreeMap;
#[cfg(feature = "semantic")]
use std::path::Path;
#[cfg(feature = "semantic")]
use std::sync::{Arc, RwLock};
#[cfg(feature = "semantic")]
use ra_ap_ide::AnalysisHost;
#[cfg(feature = "semantic")]
use ra_ap_load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at};
#[cfg(feature = "semantic")]
use ra_ap_project_model::{CargoConfig, RustLibSource};
#[cfg(feature = "semantic")]
use ra_ap_vfs::Vfs;
#[cfg(feature = "semantic")]
use super::common::with_parsed_file;
#[cfg(feature = "semantic")]
use super::file_analysis::SemanticFileAnalysis;
#[cfg(feature = "semantic")]
pub(super) type FnEntry = (Box<str>, usize, usize, bool);
#[cfg(feature = "semantic")]
pub struct SemanticContext {
pub(super) host: AnalysisHost,
pub(super) vfs: Vfs,
file_cache: RwLock<BTreeMap<Box<str>, Arc<SemanticFileAnalysis>>>,
pub(super) file_setup_count: Cell<usize>,
}
#[cfg(not(feature = "semantic"))]
pub struct SemanticContext(());
#[cfg(not(feature = "semantic"))]
pub struct SemanticFileAnalysis(());
#[cfg(feature = "semantic")]
const COPY_PRIMITIVES: &[&str] = &[
"bool", "char", "f32", "f64", "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32",
"u64", "u128", "usize",
];
#[cfg(feature = "semantic")]
impl SemanticContext {
pub fn load(workspace_root: &Path) -> Option<Self> {
let cargo_config = cargo_config_minimal();
let load_config = load_config_minimal();
let (db, vfs, _proc_macro) =
load_workspace_at(workspace_root, &cargo_config, &load_config, &|_| {}).ok()?;
let host = AnalysisHost::with_database(db);
Some(Self {
host,
vfs,
file_cache: RwLock::new(BTreeMap::new()),
file_setup_count: Cell::new(0),
})
}
pub fn file_setup_count(&self) -> usize {
self.file_setup_count.get()
}
pub fn analyze_file(&self, file: &str) -> Option<Arc<SemanticFileAnalysis>> {
let cache = match self.file_cache.read() {
Ok(cache) => cache,
Err(poisoned) => poisoned.into_inner(),
};
if let Some(cached) = cache.get(file) {
return Some(Arc::clone(cached));
}
drop(cache);
with_parsed_file(self, file, |pf| self.cache_analysis(file, pf))
}
fn cache_analysis(
&self,
file: &str,
pf: &super::common::ParsedFile<'_>,
) -> Arc<SemanticFileAnalysis> {
let cache = match self.file_cache.read() {
Ok(cache) => cache,
Err(poisoned) => poisoned.into_inner(),
};
if let Some(cached) = cache.get(file).cloned() {
return cached;
}
drop(cache);
let arc = Arc::new(SemanticFileAnalysis::build(pf));
let mut cache = match self.file_cache.write() {
Ok(cache) => cache,
Err(poisoned) => poisoned.into_inner(),
};
cache.insert(Box::from(file), Arc::clone(&arc));
arc
}
pub fn resolve_type(&self, file: &str, line: usize, col: usize) -> Option<Box<str>> {
let analysis = self.analyze_file(file)?;
analysis.resolve_type(line, col).map(Box::from)
}
pub fn is_copy(type_name: &str) -> bool {
COPY_PRIMITIVES.contains(&type_name)
}
}
#[cfg(feature = "semantic")]
fn cargo_config_minimal() -> CargoConfig {
CargoConfig {
all_targets: false,
features: Default::default(),
target: None,
sysroot: Some(RustLibSource::Discover),
sysroot_src: None,
rustc_source: None,
extra_includes: Vec::new(),
cfg_overrides: Default::default(),
wrap_rustc_in_build_scripts: false,
run_build_script_command: None,
extra_args: Vec::new(),
extra_env: Default::default(),
invocation_strategy: Default::default(),
target_dir_config: Default::default(),
set_test: false,
no_deps: false,
}
}
#[cfg(feature = "semantic")]
fn load_config_minimal() -> LoadCargoConfig {
LoadCargoConfig {
load_out_dirs_from_check: false,
with_proc_macro_server: ProcMacroServerChoice::None,
prefill_caches: false,
proc_macro_processes: 0,
num_worker_threads: 1,
}
}