Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/language_tools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ serde_json.workspace = true
settings.workspace = true
theme.workspace = true
tree-sitter.workspace = true
sysinfo.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
Expand Down
158 changes: 141 additions & 17 deletions crates/language_tools/src/lsp_button.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::{
cell::RefCell,
collections::{BTreeMap, HashMap},
path::{Path, PathBuf},
rc::Rc,
time::Duration,
time::{Duration, Instant},
};

use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System};

use client::proto;
use collections::HashSet;
use editor::{Editor, EditorEvent};
Expand Down Expand Up @@ -41,13 +44,93 @@ pub struct LspButton {
_subscriptions: Vec<Subscription>,
}

#[derive(Debug)]
struct LanguageServerState {
items: Vec<LspMenuItem>,
workspace: WeakEntity<Workspace>,
lsp_store: WeakEntity<LspStore>,
active_editor: Option<ActiveEditor>,
language_servers: LanguageServers,
process_memory_cache: Rc<RefCell<ProcessMemoryCache>>,
}

impl std::fmt::Debug for LanguageServerState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LanguageServerState")
.field("items", &self.items)
.field("workspace", &self.workspace)
.field("lsp_store", &self.lsp_store)
.field("active_editor", &self.active_editor)
.field("language_servers", &self.language_servers)
.finish_non_exhaustive()
}
}

const PROCESS_MEMORY_CACHE_DURATION: Duration = Duration::from_secs(5);

struct ProcessMemoryCache {
system: System,
memory_usage: HashMap<u32, u64>,
last_refresh: Option<Instant>,
}

impl ProcessMemoryCache {
fn new() -> Self {
Self {
system: System::new(),
memory_usage: HashMap::new(),
last_refresh: None,
}
}

fn get_memory_usage(&mut self, process_id: u32) -> u64 {
let cache_expired = self
.last_refresh
.map(|last| last.elapsed() >= PROCESS_MEMORY_CACHE_DURATION)
.unwrap_or(true);

if cache_expired {
let refresh_kind =
RefreshKind::nothing().with_processes(ProcessRefreshKind::nothing().with_memory());
self.system.refresh_specifics(refresh_kind);
self.memory_usage.clear();
self.last_refresh = Some(Instant::now());
}

if let Some(&memory) = self.memory_usage.get(&process_id) {
return memory;
}

let root_pid = Pid::from_u32(process_id);

let parent_map: HashMap<Pid, Pid> = self
.system
.processes()
.iter()
.filter_map(|(&pid, process)| Some((pid, process.parent()?)))
.collect();

let total_memory = self
.system
.processes()
.iter()
.filter(|(pid, _)| self.is_descendant_of(**pid, root_pid, &parent_map))
.map(|(_, process)| process.memory())
.sum();

self.memory_usage.insert(process_id, total_memory);
total_memory
}

fn is_descendant_of(&self, pid: Pid, root_pid: Pid, parent_map: &HashMap<Pid, Pid>) -> bool {
let mut current = pid;
while current != root_pid {
match parent_map.get(&current) {
Some(&parent) => current = parent,
None => return false,
}
}
true
}
}

struct ActiveEditor {
Expand Down Expand Up @@ -143,13 +226,16 @@ impl LanguageServerState {
(
status.server_version.clone(),
status.binary.as_ref().map(|b| b.path.clone()),
status.process_id,
),
)
})
.collect::<HashMap<_, _>>()
})
.unwrap_or_default();

let process_memory_cache = self.process_memory_cache.clone();

let mut first_button_encountered = false;
for item in &self.items {
if let LspMenuItem::ToggleServersButton { restart } = item {
Expand Down Expand Up @@ -274,16 +360,17 @@ impl LanguageServerState {
.or_else(|| server_info.binary_status.as_ref()?.message.as_ref())
.cloned();

let (server_version, binary_path) = server_metadata
let (server_version, binary_path, process_id) = server_metadata
.get(&server_info.id)
.map(|(version, path)| {
.map(|(version, path, process_id)| {
(
version.clone(),
path.as_ref()
.map(|p| SharedString::from(p.compact().to_string_lossy().to_string())),
*process_id,
)
})
.unwrap_or((None, None));
.unwrap_or((None, None, None));

let truncated_message = message.as_ref().and_then(|message| {
message
Expand All @@ -293,17 +380,6 @@ impl LanguageServerState {
.next()
});

let metadata_label = match (&server_version, &truncated_message) {
(None, None) => None,
(Some(version), None) => Some(SharedString::from(format!("v{}", version.as_ref()))),
(None, Some(message)) => Some(message.clone()),
(Some(version), Some(message)) => Some(SharedString::from(format!(
"v{}\n\n{}",
version.as_ref(),
message.as_ref()
))),
};

let submenu_server_name = server_info.name.clone();
let submenu_server_info = server_info.clone();

Expand All @@ -319,6 +395,7 @@ impl LanguageServerState {
let lsp_store = self.lsp_store.clone();
let state = cx.entity().downgrade();
let can_stop = submenu_server_info.can_stop();
let process_memory_cache = process_memory_cache.clone();

move |menu, _window, _cx| {
let mut submenu = menu;
Expand Down Expand Up @@ -509,9 +586,55 @@ impl LanguageServerState {
}

submenu = submenu.separator().custom_row({
let metadata_label = metadata_label.clone();
let binary_path = binary_path.clone();
let server_version = server_version.clone();
let truncated_message = truncated_message.clone();
let process_memory_cache = process_memory_cache.clone();
move |_, _| {
let memory_usage = process_id.map(|pid| {
process_memory_cache.borrow_mut().get_memory_usage(pid)
});

let memory_label = memory_usage.map(|bytes| {
if bytes >= 1024 * 1024 * 1024 {
format!(
"{:.1} GB",
bytes as f64 / (1024.0 * 1024.0 * 1024.0)
)
} else {
format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0))
}
});

let metadata_label =
match (&server_version, &memory_label, &truncated_message) {
(None, None, None) => None,
(Some(version), None, None) => {
Some(format!("v{}", version.as_ref()))
}
(None, Some(memory), None) => Some(memory.clone()),
(Some(version), Some(memory), None) => {
Some(format!("v{} • {}", version.as_ref(), memory))
}
(None, None, Some(message)) => Some(message.to_string()),
(Some(version), None, Some(message)) => Some(format!(
"v{}\n\n{}",
version.as_ref(),
message.as_ref()
)),
(None, Some(memory), Some(message)) => {
Some(format!("{}\n\n{}", memory, message.as_ref()))
}
(Some(version), Some(memory), Some(message)) => {
Some(format!(
"v{} • {}\n\n{}",
version.as_ref(),
memory,
message.as_ref()
))
}
};

h_flex()
.id("metadata-container")
.ml_neg_1()
Expand Down Expand Up @@ -744,6 +867,7 @@ impl LspButton {
lsp_store: lsp_store.downgrade(),
active_editor: None,
language_servers,
process_memory_cache: Rc::new(RefCell::new(ProcessMemoryCache::new())),
});

let mut lsp_button = Self {
Expand Down
1 change: 1 addition & 0 deletions crates/language_tools/src/lsp_log_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,7 @@ impl ServerInfo {
binary: Some(server.binary().clone()),
configuration: Some(server.configuration().clone()),
workspace_folders: server.workspace_folders(),
process_id: server.process_id(),
},
}
}
Expand Down
5 changes: 5 additions & 0 deletions crates/lsp/src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,11 @@ impl LanguageServer {
self.server_id
}

/// Get the process ID of the running language server, if available.
pub fn process_id(&self) -> Option<u32> {
self.server.lock().as_ref().map(|child| child.id())
}

/// Language server's binary information.
pub fn binary(&self) -> &LanguageServerBinary {
&self.binary
Expand Down
4 changes: 4 additions & 0 deletions crates/project/src/lsp_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3908,6 +3908,7 @@ pub struct LanguageServerStatus {
pub binary: Option<LanguageServerBinary>,
pub configuration: Option<Value>,
pub workspace_folders: BTreeSet<Uri>,
pub process_id: Option<u32>,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -8487,6 +8488,7 @@ impl LspStore {
binary: None,
configuration: None,
workspace_folders: BTreeSet::new(),
process_id: None,
},
)
})
Expand Down Expand Up @@ -9554,6 +9556,7 @@ impl LspStore {
binary: None,
configuration: None,
workspace_folders: BTreeSet::new(),
process_id: None,
},
);
cx.emit(LspStoreEvent::LanguageServerAdded(
Expand Down Expand Up @@ -11610,6 +11613,7 @@ impl LspStore {
binary: Some(language_server.binary().clone()),
configuration: Some(language_server.configuration().clone()),
workspace_folders: language_server.workspace_folders(),
process_id: language_server.process_id(),
},
);

Expand Down