Allow .zed/tasks.json local configs (#8536)
 Follow-up of https://github.com/zed-industries/zed/issues/7108#issuecomment-1960746397 Makes more clear where each task came from, auto (re)load .zed/config.json changes, properly filtering out other worktree tasks. Release Notes: - Added local task configurations
This commit is contained in:
parent
7f954cbbb8
commit
ac30ded80e
12 changed files with 715 additions and 281 deletions
|
@ -1,130 +0,0 @@
|
|||
use std::path::Path;
|
||||
|
||||
use fuzzy::StringMatch;
|
||||
use ui::{prelude::*, HighlightedLabel};
|
||||
use util::paths::PathExt;
|
||||
use workspace::WorkspaceLocation;
|
||||
|
||||
#[derive(Clone, IntoElement)]
|
||||
pub struct HighlightedText {
|
||||
pub text: String,
|
||||
pub highlight_positions: Vec<usize>,
|
||||
char_count: usize,
|
||||
}
|
||||
|
||||
impl HighlightedText {
|
||||
fn join(components: impl Iterator<Item = Self>, separator: &str) -> Self {
|
||||
let mut char_count = 0;
|
||||
let separator_char_count = separator.chars().count();
|
||||
let mut text = String::new();
|
||||
let mut highlight_positions = Vec::new();
|
||||
for component in components {
|
||||
if char_count != 0 {
|
||||
text.push_str(separator);
|
||||
char_count += separator_char_count;
|
||||
}
|
||||
|
||||
highlight_positions.extend(
|
||||
component
|
||||
.highlight_positions
|
||||
.iter()
|
||||
.map(|position| position + char_count),
|
||||
);
|
||||
text.push_str(&component.text);
|
||||
char_count += component.text.chars().count();
|
||||
}
|
||||
|
||||
Self {
|
||||
text,
|
||||
highlight_positions,
|
||||
char_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for HighlightedText {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
HighlightedLabel::new(self.text, self.highlight_positions)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HighlightedWorkspaceLocation {
|
||||
pub names: HighlightedText,
|
||||
pub paths: Vec<HighlightedText>,
|
||||
}
|
||||
|
||||
impl HighlightedWorkspaceLocation {
|
||||
pub fn new(string_match: &StringMatch, location: &WorkspaceLocation) -> Self {
|
||||
let mut path_start_offset = 0;
|
||||
let (names, paths): (Vec<_>, Vec<_>) = location
|
||||
.paths()
|
||||
.iter()
|
||||
.map(|path| {
|
||||
let path = path.compact();
|
||||
let highlighted_text = Self::highlights_for_path(
|
||||
path.as_ref(),
|
||||
&string_match.positions,
|
||||
path_start_offset,
|
||||
);
|
||||
|
||||
path_start_offset += highlighted_text.1.char_count;
|
||||
|
||||
highlighted_text
|
||||
})
|
||||
.unzip();
|
||||
|
||||
Self {
|
||||
names: HighlightedText::join(names.into_iter().filter_map(|name| name), ", "),
|
||||
paths,
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the highlighted text for the name and path
|
||||
fn highlights_for_path(
|
||||
path: &Path,
|
||||
match_positions: &Vec<usize>,
|
||||
path_start_offset: usize,
|
||||
) -> (Option<HighlightedText>, HighlightedText) {
|
||||
let path_string = path.to_string_lossy();
|
||||
let path_char_count = path_string.chars().count();
|
||||
// Get the subset of match highlight positions that line up with the given path.
|
||||
// Also adjusts them to start at the path start
|
||||
let path_positions = match_positions
|
||||
.iter()
|
||||
.copied()
|
||||
.skip_while(|position| *position < path_start_offset)
|
||||
.take_while(|position| *position < path_start_offset + path_char_count)
|
||||
.map(|position| position - path_start_offset)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Again subset the highlight positions to just those that line up with the file_name
|
||||
// again adjusted to the start of the file_name
|
||||
let file_name_text_and_positions = path.file_name().map(|file_name| {
|
||||
let text = file_name.to_string_lossy();
|
||||
let char_count = text.chars().count();
|
||||
let file_name_start = path_char_count - char_count;
|
||||
let highlight_positions = path_positions
|
||||
.iter()
|
||||
.copied()
|
||||
.skip_while(|position| *position < file_name_start)
|
||||
.take_while(|position| *position < file_name_start + char_count)
|
||||
.map(|position| position - file_name_start)
|
||||
.collect::<Vec<_>>();
|
||||
HighlightedText {
|
||||
text: text.to_string(),
|
||||
highlight_positions,
|
||||
char_count,
|
||||
}
|
||||
});
|
||||
|
||||
(
|
||||
file_name_text_and_positions,
|
||||
HighlightedText {
|
||||
text: path_string.to_string(),
|
||||
highlight_positions: path_positions,
|
||||
char_count: path_char_count,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
mod highlighted_workspace_location;
|
||||
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Result,
|
||||
Subscription, Task, View, ViewContext, WeakView,
|
||||
};
|
||||
use highlighted_workspace_location::HighlightedWorkspaceLocation;
|
||||
use ordered_float::OrderedFloat;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use std::sync::Arc;
|
||||
use ui::{prelude::*, tooltip_container, HighlightedLabel, ListItem, ListItemSpacing, Tooltip};
|
||||
use picker::{
|
||||
highlighted_match_with_paths::{HighlightedMatchWithPaths, HighlightedText},
|
||||
Picker, PickerDelegate,
|
||||
};
|
||||
use std::{path::Path, sync::Arc};
|
||||
use ui::{prelude::*, tooltip_container, ListItem, ListItemSpacing, Tooltip};
|
||||
use util::paths::PathExt;
|
||||
use workspace::{ModalView, Workspace, WorkspaceId, WorkspaceLocation, WORKSPACE_DB};
|
||||
|
||||
|
@ -245,32 +245,40 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
selected: bool,
|
||||
cx: &mut ViewContext<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let Some(r#match) = self.matches.get(ix) else {
|
||||
let Some(hit) = self.matches.get(ix) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let (workspace_id, location) = &self.workspaces[r#match.candidate_id];
|
||||
let highlighted_location: HighlightedWorkspaceLocation =
|
||||
HighlightedWorkspaceLocation::new(&r#match, location);
|
||||
let tooltip_highlighted_location = highlighted_location.clone();
|
||||
|
||||
let (workspace_id, location) = &self.workspaces[hit.candidate_id];
|
||||
let is_current_workspace = self.is_current_workspace(*workspace_id, cx);
|
||||
|
||||
let mut path_start_offset = 0;
|
||||
let (match_labels, paths): (Vec<_>, Vec<_>) = location
|
||||
.paths()
|
||||
.iter()
|
||||
.map(|path| {
|
||||
let path = path.compact();
|
||||
let highlighted_text =
|
||||
highlights_for_path(path.as_ref(), &hit.positions, path_start_offset);
|
||||
|
||||
path_start_offset += highlighted_text.1.char_count;
|
||||
highlighted_text
|
||||
})
|
||||
.unzip();
|
||||
|
||||
let highlighted_match = HighlightedMatchWithPaths {
|
||||
match_label: HighlightedText::join(
|
||||
match_labels.into_iter().filter_map(|name| name),
|
||||
", ",
|
||||
),
|
||||
paths: if self.render_paths { paths } else { Vec::new() },
|
||||
};
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
.inset(true)
|
||||
.spacing(ListItemSpacing::Sparse)
|
||||
.selected(selected)
|
||||
.child(
|
||||
v_flex()
|
||||
.child(highlighted_location.names)
|
||||
.when(self.render_paths, |this| {
|
||||
this.children(highlighted_location.paths.into_iter().map(|path| {
|
||||
HighlightedLabel::new(path.text, path.highlight_positions)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted)
|
||||
}))
|
||||
}),
|
||||
)
|
||||
.child(highlighted_match.clone().render(cx))
|
||||
.when(!is_current_workspace, |el| {
|
||||
let delete_button = div()
|
||||
.child(
|
||||
|
@ -293,7 +301,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
}
|
||||
})
|
||||
.tooltip(move |cx| {
|
||||
let tooltip_highlighted_location = tooltip_highlighted_location.clone();
|
||||
let tooltip_highlighted_location = highlighted_match.clone();
|
||||
cx.new_view(move |_| MatchTooltip {
|
||||
highlighted_location: tooltip_highlighted_location,
|
||||
})
|
||||
|
@ -303,6 +311,54 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
// Compute the highlighted text for the name and path
|
||||
fn highlights_for_path(
|
||||
path: &Path,
|
||||
match_positions: &Vec<usize>,
|
||||
path_start_offset: usize,
|
||||
) -> (Option<HighlightedText>, HighlightedText) {
|
||||
let path_string = path.to_string_lossy();
|
||||
let path_char_count = path_string.chars().count();
|
||||
// Get the subset of match highlight positions that line up with the given path.
|
||||
// Also adjusts them to start at the path start
|
||||
let path_positions = match_positions
|
||||
.iter()
|
||||
.copied()
|
||||
.skip_while(|position| *position < path_start_offset)
|
||||
.take_while(|position| *position < path_start_offset + path_char_count)
|
||||
.map(|position| position - path_start_offset)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Again subset the highlight positions to just those that line up with the file_name
|
||||
// again adjusted to the start of the file_name
|
||||
let file_name_text_and_positions = path.file_name().map(|file_name| {
|
||||
let text = file_name.to_string_lossy();
|
||||
let char_count = text.chars().count();
|
||||
let file_name_start = path_char_count - char_count;
|
||||
let highlight_positions = path_positions
|
||||
.iter()
|
||||
.copied()
|
||||
.skip_while(|position| *position < file_name_start)
|
||||
.take_while(|position| *position < file_name_start + char_count)
|
||||
.map(|position| position - file_name_start)
|
||||
.collect::<Vec<_>>();
|
||||
HighlightedText {
|
||||
text: text.to_string(),
|
||||
highlight_positions,
|
||||
char_count,
|
||||
}
|
||||
});
|
||||
|
||||
(
|
||||
file_name_text_and_positions,
|
||||
HighlightedText {
|
||||
text: path_string.to_string(),
|
||||
highlight_positions: path_positions,
|
||||
char_count: path_char_count,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
impl RecentProjectsDelegate {
|
||||
fn delete_recent_project(&self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
|
||||
if let Some(selected_match) = self.matches.get(ix) {
|
||||
|
@ -340,23 +396,13 @@ impl RecentProjectsDelegate {
|
|||
}
|
||||
}
|
||||
struct MatchTooltip {
|
||||
highlighted_location: HighlightedWorkspaceLocation,
|
||||
highlighted_location: HighlightedMatchWithPaths,
|
||||
}
|
||||
|
||||
impl Render for MatchTooltip {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
tooltip_container(cx, |div, _| {
|
||||
div.children(
|
||||
self.highlighted_location
|
||||
.paths
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|path| {
|
||||
HighlightedLabel::new(path.text, path.highlight_positions)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted)
|
||||
}),
|
||||
)
|
||||
self.highlighted_location.render_paths_children(div)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue