use super::super::super::picker::ProjectStatus;
use super::super::*;
use super::args_key;
impl Editor {
pub(crate) fn lsp_warm_scope_projects(&mut self, projects: Vec<(PathBuf, String)>) {
if !self.lsp_state.attach.enabled {
return;
}
if self
.picker
.as_ref()
.is_none_or(|glue| glue.picker.kind != strop_picker::Kind::WorkspaceSymbols)
{
return;
}
for project in projects {
if !self.lsp_state.attach.warm_queue.contains(&project) {
self.lsp_state.attach.warm_queue.push_back(project);
}
}
self.lsp_drain_warm_queue();
}
fn lsp_drain_warm_queue(&mut self) {
const WARM_ATTACH_LIMIT: usize = 4;
while strop_core::searchguard::warm_slot_free(
self.lsp_state.attach.pending.len(),
WARM_ATTACH_LIMIT,
) {
let Some((root, marker)) = self.lsp_state.attach.warm_queue.pop_front() else {
break;
};
if self
.picker
.as_ref()
.is_none_or(|glue| glue.picker.kind != strop_picker::Kind::WorkspaceSymbols)
{
self.lsp_state.attach.warm_queue.clear();
return;
}
self.lsp_warm_attach(root, marker);
}
}
fn lsp_warm_attach(&mut self, root: PathBuf, marker: String) {
let Some((language, ext)) = warm_language(&marker) else {
self.record_project_status(root, ProjectStatus::ambiguous(&marker));
return;
};
let target = Filesystem::Local;
let key = attach::AttachKey {
target: target.clone(),
language: language.to_string(),
path: root.clone(),
};
if self.lsp_state.attach.attached.iter().any(|attachment| {
attachment.language == language
&& attachment.root == root
&& attachment.target == target
}) || self.lsp_state.attach.pending.contains_key(&key)
{
self.record_project_status(root, ProjectStatus::healthy());
return;
}
let refusal = self.lsp_state.attach.refused.get(&key).cloned();
match refusal {
Some(attach::AttachDecision::TrustRequired { .. })
| Some(attach::AttachDecision::TrustError { .. })
| Some(attach::AttachDecision::RemoteIo { .. }) => {}
Some(decision) => {
self.record_project_status(
root,
ProjectStatus::from_decision(&decision, &key.language),
);
return;
}
None => {}
}
let ticket = match self.worker_ids.allocate() {
Ok(ticket) => ticket,
Err(error) => {
self.message = error.message;
return;
}
};
self.lsp_state.attach.pending.insert(key.clone(), ticket);
self.lsp_state.attach.warm_attempts.insert(key);
let args = attach::AttachArgs {
ticket,
path: root.clone(),
language: language.to_string(),
target,
};
match self.tape.request("lsp.attach", &args) {
Ok(true) => self.lsp_spawn_discovery(
ticket,
strop_workspace::ResourceLocation::local(root),
ext.to_string(),
language,
),
Ok(false) => {}
Err(error) => {
self.lsp_state.attach.pending.remove(&args_key(&args));
self.lsp_state.attach.warm_attempts.remove(&args_key(&args));
self.message = format!("lsp attach diverged from trace: {error}");
}
}
}
pub(super) fn lsp_warm_progress(&mut self) {
if !self.lsp_state.attach.warm_queue.is_empty() {
self.lsp_drain_warm_queue();
}
}
}
pub(crate) fn warm_language(marker: &str) -> Option<(&'static str, &'static str)> {
match marker {
"Cargo.toml" => Some(("rust", ".rs")),
"pyproject.toml" | "setup.py" => Some(("python", ".py")),
"CMakeLists.txt" => Some(("cpp", ".cpp")),
"go.mod" => Some(("go", ".go")),
_ => None,
}
}