use std::{collections::BTreeMap, mem, ops::Range, sync::Arc}; use clock::Global;use collections::{HashMap, HashSet};use gpui::{ App, AppContext as _, AsyncWindowContext, ClickEvent, Context, Entity, Focusable as _, MouseButton, Task, Window,};use language::{Buffer, BufferRow, Runnable};use lsp::LanguageServerName;use multi_buffer::{ Anchor, BufferOffset, MultiBufferOffset, MultiBufferRow, MultiBufferSnapshot, ToPoint as _,};use project::{ Location, Project, TaskSourceKind, debugger::breakpoint_store::{Breakpoint, BreakpointSessionState}, project_settings::ProjectSettings,};use settings::Settings as _;use smallvec::SmallVec;use task::{ResolvedTask, RunnableTag, TaskContext, TaskTemplate, TaskVariables, VariableName};use text::{BufferId, OffsetRangeExt as _, ToOffset as _, ToPoint as _};use ui::{Clickable as _, Color, IconButton, IconSize, Toggleable as _}; use crate::{ CodeActionSource, Editor, EditorSettings, EditorStyle, RangeToAnchorExt, SpawnNearestTask, ToggleCodeActions, UPDATE_DEBOUNCE, display_map::DisplayRow,}; #[derive(Debug)]pub(super) struct RunnableData { runnables: HashMap<BufferId, (Global, BTreeMap<BufferRow, RunnableTasks>)>, invalidate_buffer_data: HashSet<BufferId>, runnables_update_task: Task<()>,} impl RunnableData { pub fn new() -> Self { Self { runnables: HashMap::default(), invalidate_buffer_data: HashSet::default(), runnables_update_task: Task::ready(()), } } pub fn runnables( &self, (buffer_id, buffer_row): (BufferId, BufferRow), ) -> Option<&RunnableTasks> { self.runnables.get(&buffer_id)?.1.get(&buffer_row) } pub fn all_runnables(&self) -> impl Iterator<Item = &RunnableTasks> { self.runnables .values() .flat_map(|(_, tasks)| tasks.values()) } pub fn has_cached(&self, buffer_id: BufferId, version: &Global) -> bool { self.runnables .get(&buffer_id) .is_some_and(|(cached_version, _)| !version.changed_since(cached_version)) } #[cfg(test)] pub fn insert( &mut self, buffer_id: BufferId, buffer_row: BufferRow, version: Global, tasks: RunnableTasks, ) { self.runnables .entry(buffer_id) .or_insert_with(|| (version, BTreeMap::default())) .1 .insert(buffer_row, tasks); }} #[derive(Clone, Debug)]pub struct RunnableTasks { pub templates: Vec<(TaskSourceKind, TaskTemplate)>, pub offset: multi_buffer::Anchor, // We need the column at which the task context evaluation should take place (when we're spawning it via gutter). pub column: u32, // Values of all named captures, including those starting with '_' pub extra_variables: HashMap<String, String>, // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal. pub context_range: Range<BufferOffset>,} impl RunnableTasks { pub fn resolve<'a>( &'a self, cx: &'a task::TaskContext, ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a { self.templates.iter().filter_map(|(kind, template)| { template .resolve_task(&kind.to_id_base(), cx) .map(|task| (kind.clone(), task)) }) }} #[derive(Clone)]pub struct ResolvedTasks { pub templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>, pub position: Anchor,} impl Editor { pub fn refresh_runnables( &mut self, invalidate_buffer_data: Option<BufferId>, window: &mut Window, cx: &mut Context<Self>, ) { if !self.mode().is_full() || !EditorSettings::get_global(cx).gutter.runnables || !self.enable_runnables { self.clear_runnables(None); return; } if let Some(buffer) = self.buffer().read(cx).as_singleton() { let buffer_id = buffer.read(cx).remote_id(); if invalidate_buffer_data != Some(buffer_id) && self .runnables .has_cached(buffer_id, &buffer.read(cx).version()) { return; } } if let Some(buffer_id) = invalidate_buffer_data { self.runnables.invalidate_buffer_data.insert(buffer_id); } let project = self.project().map(Entity::downgrade); let lsp_task_sources = self.lsp_task_sources(true, true, cx); let multi_buffer = self.buffer.downgrade(); self.runnables.runnables_update_task = cx.spawn_in(window, async move |editor, cx| { cx.background_executor().timer(UPDATE_DEBOUNCE).await; let Some(project) = project.and_then(|p| p.upgrade()) else { return; }; let hide_runnables = project.update(cx, |project, _| project.is_via_collab()); if hide_runnables { return; } let lsp_tasks = if lsp_task_sources.is_empty() { Vec::new() } else { let Ok(lsp_tasks) = cx .update(|_, cx| crate::lsp_tasks(project.clone(), &lsp_task_sources, None, cx)) else { return; }; lsp_tasks.await }; let new_rows = { let Some((multi_buffer_snapshot, multi_buffer_query_range)) = editor .update(cx, |editor, cx| { let multi_buffer = editor.buffer().read(cx); if multi_buffer.is_singleton() { Some((multi_buffer.snapshot(cx), Anchor::min()..Anchor::max())) } else { let display_snapshot = editor.display_map.update(cx, |map, cx| map.snapshot(cx)); let multi_buffer_query_range = editor.multi_buffer_visible_range(&display_snapshot, cx); let multi_buffer_snapshot = display_snapshot.buffer(); Some(( multi_buffer_snapshot.clone(), multi_buffer_query_range.to_anchors(&multi_buffer_snapshot), )) } }) .ok() .flatten() else { return; }; cx.background_spawn({ async move { multi_buffer_snapshot .runnable_ranges(multi_buffer_query_range) .collect() } }) .await }; let Ok(multi_buffer_snapshot) = editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx)) else { return; }; let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| { lsp_tasks .into_iter() .flat_map(|(kind, tasks)| { tasks.into_iter().filter_map(move |(location, task)| { Some((kind.clone(), location?, task)) }) }) .fold(HashMap::default(), |mut acc, (kind, location, task)| { let buffer = location.target.buffer; let buffer_snapshot = buffer.read(cx).snapshot(); let offset = multi_buffer_snapshot.excerpts().find_map( |(excerpt_id, snapshot, _)| { if snapshot.remote_id() == buffer_snapshot.remote_id() { multi_buffer_snapshot .anchor_in_excerpt(excerpt_id, location.target.range.start) } else { None } }, ); if let Some(offset) = offset { let task_buffer_range = location.target.range.to_point(&buffer_snapshot); let context_buffer_range = task_buffer_range.to_offset(&buffer_snapshot); let context_range = BufferOffset(context_buffer_range.start) ..BufferOffset(context_buffer_range.end); acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row)) .or_insert_with(|| RunnableTasks { templates: Vec::new(), offset, column: task_buffer_range.start.column, extra_variables: HashMap::default(), context_range, }) .templates .push((kind, task.original_task().clone())); } acc }) }) else { return; }; let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| { buffer.language_settings(cx).tasks.prefer_lsp }) else { return; }; let rows = Self::runnable_rows( project, multi_buffer_snapshot, prefer_lsp && !lsp_tasks_by_rows.is_empty(), new_rows, cx.clone(), ) .await; editor .update(cx, |editor, cx| { for buffer_id in std::mem::take(&mut editor.runnables.invalidate_buffer_data) { editor.clear_runnables(Some(buffer_id)); } for ((buffer_id, row), mut new_tasks) in rows { let Some(buffer) = editor.buffer().read(cx).buffer(buffer_id) else { continue; }; if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&(buffer_id, row)) { new_tasks.templates.extend(lsp_tasks.templates); } editor.insert_runnables( buffer_id, buffer.read(cx).version(), row, new_tasks, ); } for ((buffer_id, row), new_tasks) in lsp_tasks_by_rows { let Some(buffer) = editor.buffer().read(cx).buffer(buffer_id) else { continue; }; editor.insert_runnables( buffer_id, buffer.read(cx).version(), row, new_tasks, ); } }) .ok(); }); } pub fn spawn_nearest_task( &mut self, action: &SpawnNearestTask, window: &mut Window, cx: &mut Context<Self>, ) { let Some((workspace, _)) = self.workspace.clone() else { return; }; let Some(project) = self.project.clone() else { return; }; // Try to find a closest, enclosing node using tree-sitter that has a task let Some((buffer, buffer_row, tasks)) = self .find_enclosing_node_task(cx) // Or find the task that's closest in row-distance. .or_else(|| self.find_closest_task(cx)) else { return; }; let reveal_strategy = action.reveal; let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx); cx.spawn_in(window, async move |_, cx| { let context = task_context.await?; let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?; let resolved = &mut resolved_task.resolved; resolved.reveal = reveal_strategy; workspace .update_in(cx, |workspace, window, cx| { workspace.schedule_resolved_task( task_source_kind, resolved_task, false, window, cx, ); }) .ok() }) .detach(); } pub fn clear_runnables(&mut self, for_buffer: Option<BufferId>) { if let Some(buffer_id) = for_buffer { self.runnables.runnables.remove(&buffer_id); } else { self.runnables.runnables.clear(); } self.runnables.invalidate_buffer_data.clear(); self.runnables.runnables_update_task = Task::ready(()); } pub fn task_context(&self, window: &mut Window, cx: &mut App) -> Task<Option<TaskContext>> { let Some(project) = self.project.clone() else { return Task::ready(None); }; let (selection, buffer, editor_snapshot) = { let selection = self.selections.newest_adjusted(&self.display_snapshot(cx)); let Some((buffer, _)) = self .buffer() .read(cx) .point_to_buffer_offset(selection.start, cx) else { return Task::ready(None); }; let snapshot = self.snapshot(window, cx); (selection, buffer, snapshot) }; let selection_range = selection.range(); let start = editor_snapshot .display_snapshot .buffer_snapshot() .anchor_after(selection_range.start) .text_anchor; let end = editor_snapshot .display_snapshot .buffer_snapshot() .anchor_after(selection_range.end) .text_anchor; let location = Location { buffer, range: start..end, }; let captured_variables = { let mut variables = TaskVariables::default(); let buffer = location.buffer.read(cx); let buffer_id = buffer.remote_id(); let snapshot = buffer.snapshot(); let starting_point = location.range.start.to_point(&snapshot); let starting_offset = starting_point.to_offset(&snapshot); for (_, tasks) in self .runnables .runnables .get(&buffer_id) .into_iter() .flat_map(|(_, tasks)| tasks.range(0..starting_point.row + 1)) { if !tasks .context_range .contains(&crate::BufferOffset(starting_offset)) { continue; } for (capture_name, value) in tasks.extra_variables.iter() { variables.insert( VariableName::Custom(capture_name.to_owned().into()), value.clone(), ); } } variables }; project.update(cx, |project, cx| { project.task_store().update(cx, |task_store, cx| { task_store.task_context_for_location(captured_variables, location, cx) }) }) } pub fn lsp_task_sources( &self, visible_only: bool, skip_cached: bool, cx: &mut Context<Self>, ) -> HashMap<LanguageServerName, Vec<BufferId>> { if !self.lsp_data_enabled() { return HashMap::default(); } let buffers = if visible_only { self.visible_excerpts(true, cx) .into_values() .map(|(buffer, _, _)| buffer) .collect() } else { self.buffer().read(cx).all_buffers() }; let lsp_settings = &ProjectSettings::get_global(cx).lsp; buffers .into_iter() .filter_map(|buffer| { let lsp_tasks_source = buffer .read(cx) .language()? .context_provider()? .lsp_task_source()?; if lsp_settings .get(&lsp_tasks_source) .is_none_or(|s| s.enable_lsp_tasks) { let buffer_id = buffer.read(cx).remote_id(); if skip_cached && self .runnables .has_cached(buffer_id, &buffer.read(cx).version()) { None } else { Some((lsp_tasks_source, buffer_id)) } } else { None } }) .fold( HashMap::default(), |mut acc, (lsp_task_source, buffer_id)| { acc.entry(lsp_task_source) .or_insert_with(Vec::new) .push(buffer_id); acc }, ) } pub fn find_enclosing_node_task( &mut self, cx: &mut Context<Self>, ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> { let snapshot = self.buffer.read(cx).snapshot(cx); let offset = self .selections .newest::<MultiBufferOffset>(&self.display_snapshot(cx)) .head(); let mut excerpt = snapshot.excerpt_containing(offset..offset)?; let offset = excerpt.map_offset_to_buffer(offset); let buffer_id = excerpt.buffer().remote_id(); let layer = excerpt.buffer().syntax_layer_at(offset)?; let mut cursor = layer.node().walk(); while cursor.goto_first_child_for_byte(offset.0).is_some() { if cursor.node().end_byte() == offset.0 { cursor.goto_next_sibling(); } }Lines 1–500 of 1093. Load more
use std::{collections::BTreeMap, mem, ops::Range, sync::Arc}; use clock::Global; use collections::{HashMap, HashSet}; use gpui::{ App, AppContext as _, AsyncWindowContext, ClickEvent, Context, Entity, Focusable as _, MouseButton, Task, Window, }; use language::{Buffer, BufferRow, Runnable}; use lsp::LanguageServerName; use multi_buffer::{ Anchor, BufferOffset, MultiBufferOffset, MultiBufferRow, MultiBufferSnapshot, ToPoint as _, }; use project::{ Location, Project, TaskSourceKind, debugger::breakpoint_store::{Breakpoint, BreakpointSessionState}, project_settings::ProjectSettings, }; use settings::Settings as _; use smallvec::SmallVec; use task::{ResolvedTask, RunnableTag, TaskContext, TaskTemplate, TaskVariables, VariableName}; use text::{BufferId, OffsetRangeExt as _, ToOffset as _, ToPoint as _}; use ui::{Clickable as _, Color, IconButton, IconSize, Toggleable as _}; use crate::{ CodeActionSource, Editor, EditorSettings, EditorStyle, RangeToAnchorExt, SpawnNearestTask, ToggleCodeActions, UPDATE_DEBOUNCE, display_map::DisplayRow, }; #[derive(Debug)] pub(super) struct RunnableData { runnables: HashMap<BufferId, (Global, BTreeMap<BufferRow, RunnableTasks>)>, invalidate_buffer_data: HashSet<BufferId>, runnables_update_task: Task<()>, } impl RunnableData { pub fn new() -> Self { Self { runnables: HashMap::default(), invalidate_buffer_data: HashSet::default(), runnables_update_task: Task::ready(()), } } pub fn runnables( &self, (buffer_id, buffer_row): (BufferId, BufferRow), ) -> Option<&RunnableTasks> { self.runnables.get(&buffer_id)?.1.get(&buffer_row) } pub fn all_runnables(&self) -> impl Iterator<Item = &RunnableTasks> { self.runnables .values() .flat_map(|(_, tasks)| tasks.values()) } pub fn has_cached(&self, buffer_id: BufferId, version: &Global) -> bool { self.runnables .get(&buffer_id) .is_some_and(|(cached_version, _)| !version.changed_since(cached_version)) } #[cfg(test)] pub fn insert( &mut self, buffer_id: BufferId, buffer_row: BufferRow, version: Global, tasks: RunnableTasks, ) { self.runnables .entry(buffer_id) .or_insert_with(|| (version, BTreeMap::default())) .1 .insert(buffer_row, tasks); } } #[derive(Clone, Debug)] pub struct RunnableTasks { pub templates: Vec<(TaskSourceKind, TaskTemplate)>, pub offset: multi_buffer::Anchor, // We need the column at which the task context evaluation should take place (when we're spawning it via gutter). pub column: u32, // Values of all named captures, including those starting with '_' pub extra_variables: HashMap<String, String>, // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal. pub context_range: Range<BufferOffset>, } impl RunnableTasks { pub fn resolve<'a>( &'a self, cx: &'a task::TaskContext, ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a { self.templates.iter().filter_map(|(kind, template)| { template .resolve_task(&kind.to_id_base(), cx) .map(|task| (kind.clone(), task)) }) } } #[derive(Clone)] pub struct ResolvedTasks { pub templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>, pub position: Anchor, } impl Editor { pub fn refresh_runnables( &mut self, invalidate_buffer_data: Option<BufferId>, window: &mut Window, cx: &mut Context<Self>, ) { if !self.mode().is_full() || !EditorSettings::get_global(cx).gutter.runnables || !self.enable_runnables { self.clear_runnables(None); return; } if let Some(buffer) = self.buffer().read(cx).as_singleton() { let buffer_id = buffer.read(cx).remote_id(); if invalidate_buffer_data != Some(buffer_id) && self .runnables .has_cached(buffer_id, &buffer.read(cx).version()) { return; } } if let Some(buffer_id) = invalidate_buffer_data { self.runnables.invalidate_buffer_data.insert(buffer_id); } let project = self.project().map(Entity::downgrade); let lsp_task_sources = self.lsp_task_sources(true, true, cx); let multi_buffer = self.buffer.downgrade(); self.runnables.runnables_update_task = cx.spawn_in(window, async move |editor, cx| { cx.background_executor().timer(UPDATE_DEBOUNCE).await; let Some(project) = project.and_then(|p| p.upgrade()) else { return; }; let hide_runnables = project.update(cx, |project, _| project.is_via_collab()); if hide_runnables { return; } let lsp_tasks = if lsp_task_sources.is_empty() { Vec::new() } else { let Ok(lsp_tasks) = cx .update(|_, cx| crate::lsp_tasks(project.clone(), &lsp_task_sources, None, cx)) else { return; }; lsp_tasks.await }; let new_rows = { let Some((multi_buffer_snapshot, multi_buffer_query_range)) = editor .update(cx, |editor, cx| { let multi_buffer = editor.buffer().read(cx); if multi_buffer.is_singleton() { Some((multi_buffer.snapshot(cx), Anchor::min()..Anchor::max())) } else { let display_snapshot = editor.display_map.update(cx, |map, cx| map.snapshot(cx)); let multi_buffer_query_range = editor.multi_buffer_visible_range(&display_snapshot, cx); let multi_buffer_snapshot = display_snapshot.buffer(); Some(( multi_buffer_snapshot.clone(), multi_buffer_query_range.to_anchors(&multi_buffer_snapshot), )) } }) .ok() .flatten() else { return; }; cx.background_spawn({ async move { multi_buffer_snapshot .runnable_ranges(multi_buffer_query_range) .collect() } }) .await }; let Ok(multi_buffer_snapshot) = editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx)) else { return; }; let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| { lsp_tasks .into_iter() .flat_map(|(kind, tasks)| { tasks.into_iter().filter_map(move |(location, task)| { Some((kind.clone(), location?, task)) }) }) .fold(HashMap::default(), |mut acc, (kind, location, task)| { let buffer = location.target.buffer; let buffer_snapshot = buffer.read(cx).snapshot(); let offset = multi_buffer_snapshot.excerpts().find_map( |(excerpt_id, snapshot, _)| { if snapshot.remote_id() == buffer_snapshot.remote_id() { multi_buffer_snapshot .anchor_in_excerpt(excerpt_id, location.target.range.start) } else { None } }, ); if let Some(offset) = offset { let task_buffer_range = location.target.range.to_point(&buffer_snapshot); let context_buffer_range = task_buffer_range.to_offset(&buffer_snapshot); let context_range = BufferOffset(context_buffer_range.start) ..BufferOffset(context_buffer_range.end); acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row)) .or_insert_with(|| RunnableTasks { templates: Vec::new(), offset, column: task_buffer_range.start.column, extra_variables: HashMap::default(), context_range, }) .templates .push((kind, task.original_task().clone())); } acc }) }) else { return; }; let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| { buffer.language_settings(cx).tasks.prefer_lsp }) else { return; }; let rows = Self::runnable_rows( project, multi_buffer_snapshot, prefer_lsp && !lsp_tasks_by_rows.is_empty(), new_rows, cx.clone(), ) .await; editor .update(cx, |editor, cx| { for buffer_id in std::mem::take(&mut editor.runnables.invalidate_buffer_data) { editor.clear_runnables(Some(buffer_id)); } for ((buffer_id, row), mut new_tasks) in rows { let Some(buffer) = editor.buffer().read(cx).buffer(buffer_id) else { continue; }; if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&(buffer_id, row)) { new_tasks.templates.extend(lsp_tasks.templates); } editor.insert_runnables( buffer_id, buffer.read(cx).version(), row, new_tasks, ); } for ((buffer_id, row), new_tasks) in lsp_tasks_by_rows { let Some(buffer) = editor.buffer().read(cx).buffer(buffer_id) else { continue; }; editor.insert_runnables( buffer_id, buffer.read(cx).version(), row, new_tasks, ); } }) .ok(); }); } pub fn spawn_nearest_task( &mut self, action: &SpawnNearestTask, window: &mut Window, cx: &mut Context<Self>, ) { let Some((workspace, _)) = self.workspace.clone() else { return; }; let Some(project) = self.project.clone() else { return; }; // Try to find a closest, enclosing node using tree-sitter that has a task let Some((buffer, buffer_row, tasks)) = self .find_enclosing_node_task(cx) // Or find the task that's closest in row-distance. .or_else(|| self.find_closest_task(cx)) else { return; }; let reveal_strategy = action.reveal; let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx); cx.spawn_in(window, async move |_, cx| { let context = task_context.await?; let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?; let resolved = &mut resolved_task.resolved; resolved.reveal = reveal_strategy; workspace .update_in(cx, |workspace, window, cx| { workspace.schedule_resolved_task( task_source_kind, resolved_task, false, window, cx, ); }) .ok() }) .detach(); } pub fn clear_runnables(&mut self, for_buffer: Option<BufferId>) { if let Some(buffer_id) = for_buffer { self.runnables.runnables.remove(&buffer_id); } else { self.runnables.runnables.clear(); } self.runnables.invalidate_buffer_data.clear(); self.runnables.runnables_update_task = Task::ready(()); } pub fn task_context(&self, window: &mut Window, cx: &mut App) -> Task<Option<TaskContext>> { let Some(project) = self.project.clone() else { return Task::ready(None); }; let (selection, buffer, editor_snapshot) = { let selection = self.selections.newest_adjusted(&self.display_snapshot(cx)); let Some((buffer, _)) = self .buffer() .read(cx) .point_to_buffer_offset(selection.start, cx) else { return Task::ready(None); }; let snapshot = self.snapshot(window, cx); (selection, buffer, snapshot) }; let selection_range = selection.range(); let start = editor_snapshot .display_snapshot .buffer_snapshot() .anchor_after(selection_range.start) .text_anchor; let end = editor_snapshot .display_snapshot .buffer_snapshot() .anchor_after(selection_range.end) .text_anchor; let location = Location { buffer, range: start..end, }; let captured_variables = { let mut variables = TaskVariables::default(); let buffer = location.buffer.read(cx); let buffer_id = buffer.remote_id(); let snapshot = buffer.snapshot(); let starting_point = location.range.start.to_point(&snapshot); let starting_offset = starting_point.to_offset(&snapshot); for (_, tasks) in self .runnables .runnables .get(&buffer_id) .into_iter() .flat_map(|(_, tasks)| tasks.range(0..starting_point.row + 1)) { if !tasks .context_range .contains(&crate::BufferOffset(starting_offset)) { continue; } for (capture_name, value) in tasks.extra_variables.iter() { variables.insert( VariableName::Custom(capture_name.to_owned().into()), value.clone(), ); } } variables }; project.update(cx, |project, cx| { project.task_store().update(cx, |task_store, cx| { task_store.task_context_for_location(captured_variables, location, cx) }) }) } pub fn lsp_task_sources( &self, visible_only: bool, skip_cached: bool, cx: &mut Context<Self>, ) -> HashMap<LanguageServerName, Vec<BufferId>> { if !self.lsp_data_enabled() { return HashMap::default(); } let buffers = if visible_only { self.visible_excerpts(true, cx) .into_values() .map(|(buffer, _, _)| buffer) .collect() } else { self.buffer().read(cx).all_buffers() }; let lsp_settings = &ProjectSettings::get_global(cx).lsp; buffers .into_iter() .filter_map(|buffer| { let lsp_tasks_source = buffer .read(cx) .language()? .context_provider()? .lsp_task_source()?; if lsp_settings .get(&lsp_tasks_source) .is_none_or(|s| s.enable_lsp_tasks) { let buffer_id = buffer.read(cx).remote_id(); if skip_cached && self .runnables .has_cached(buffer_id, &buffer.read(cx).version()) { None } else { Some((lsp_tasks_source, buffer_id)) } } else { None } }) .fold( HashMap::default(), |mut acc, (lsp_task_source, buffer_id)| { acc.entry(lsp_task_source) .or_insert_with(Vec::new) .push(buffer_id); acc }, ) } pub fn find_enclosing_node_task( &mut self, cx: &mut Context<Self>, ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> { let snapshot = self.buffer.read(cx).snapshot(cx); let offset = self .selections .newest::<MultiBufferOffset>(&self.display_snapshot(cx)) .head(); let mut excerpt = snapshot.excerpt_containing(offset..offset)?; let offset = excerpt.map_offset_to_buffer(offset); let buffer_id = excerpt.buffer().remote_id(); let layer = excerpt.buffer().syntax_layer_at(offset)?; let mut cursor = layer.node().walk(); while cursor.goto_first_child_for_byte(offset.0).is_some() { if cursor.node().end_byte() == offset.0 { cursor.goto_next_sibling(); } }