Display buffer/project search entries in the outline panel (#16589)

Prototypes a way to display new entities in the outline panel, making it
less outline.
The design is not final and might be adjusted, but the workflow seems to
be solid enough to keep and iron it out.

* Now, when any project search buffer is activated (multi buffer mode),
or buffer search is open (singleton buffer mode, but is available for
search usages multi buffer too — in that case buffer search overrides
multi buffer's contents display), outline panel displays all search
matches instead of the outline items.

Outline items are not displayed at all during those cases, unless the
buffer search is closed, or a new buffer gets opened, of an active
buffer search matches zero items.


https://github.com/user-attachments/assets/4a3e4faa-7f75-4522-96bb-3761872c753a


* For the multi buffer mode, search matches are grouped under
directories and files, same as outline items

![Screenshot 2024-08-21 at 14 55
01](https://github.com/user-attachments/assets/6dac75e4-be4e-4338-917b-37a32c285b71)


* For buffer search , search matches are displayed one under another


![image](https://github.com/user-attachments/assets/9efcff85-d4c7-4462-9ef5-f76b08e59f20)


For both cases, the entire match line is taken and rendered, with the
hover tooltip showing the line number.
So far it does not look very bad, but I am certain there are bad cases
with long lines and bad indents where it looks not optimal — this part
most probably will be redesigned after some trial.
Or, maybe, it's ok to leave the current state if the horizontal
scrollbar is added?

Clicking the item navigates to the item's position in the editor.
Search item lines are also possible to filter with the outline panel's
filter input.

* Inline panel is now possible to "pin" to track a currently active
editor, to display outlines/search results for that editor even if
another item is activated afterwards:


![image](https://github.com/user-attachments/assets/75fb78c3-0e5f-47b4-ba3a-485c71d7e342)

This is useful in combination with project search results display: now
it's possible to leave the search results pinned in the outline panel
and jump to every search result and back.

If the item the panel was pinned to gets closed, the panel gets back to
its regular state, showing outlines/search results for a currently
active editor.


Release Notes:

- Added a way to display buffer/project search entries in the outline
panel
This commit is contained in:
Kirill Bulatov 2024-08-25 21:40:02 +03:00 committed by GitHub
parent dd8d52f4f4
commit 28271a9a36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1328 additions and 605 deletions

2
Cargo.lock generated
View File

@ -7387,9 +7387,11 @@ dependencies = [
"menu",
"project",
"schemars",
"search",
"serde",
"serde_json",
"settings",
"theme",
"util",
"workspace",
"worktree",

1
assets/icons/pin.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pin"><path d="M12 17v5"/><path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>

After

Width:  |  Height:  |  Size: 447 B

1
assets/icons/unpin.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pin-off"><path d="M12 17v5"/><path d="M15 9.34V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H7.89"/><path d="m2 2 20 20"/><path d="M9 9v1.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h11"/></svg>

After

Width:  |  Height:  |  Size: 401 B

View File

@ -524,7 +524,7 @@
"ctrl-alt-c": "outline_panel::CopyPath",
"alt-ctrl-shift-c": "outline_panel::CopyRelativePath",
"alt-ctrl-r": "outline_panel::RevealInFileManager",
"space": "outline_panel::Open",
"space": ["outline_panel::Open", { "change_selection": false }],
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev"
}

View File

@ -536,7 +536,7 @@
"cmd-alt-c": "outline_panel::CopyPath",
"alt-cmd-shift-c": "outline_panel::CopyRelativePath",
"alt-cmd-r": "outline_panel::RevealInFileManager",
"space": "outline_panel::Open",
"space": ["outline_panel::Open", { "change_selection": false }],
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev"
}

View File

@ -1144,16 +1144,37 @@ pub(crate) enum BufferSearchHighlights {}
impl SearchableItem for Editor {
type Match = Range<Anchor>;
fn get_matches(&self, _: &mut WindowContext) -> Vec<Range<Anchor>> {
self.background_highlights
.get(&TypeId::of::<BufferSearchHighlights>())
.map_or(Vec::new(), |(_color, ranges)| {
ranges.iter().map(|range| range.clone()).collect()
})
}
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
self.clear_background_highlights::<BufferSearchHighlights>(cx);
if self
.clear_background_highlights::<BufferSearchHighlights>(cx)
.is_some()
{
cx.emit(SearchEvent::MatchesInvalidated);
}
}
fn update_matches(&mut self, matches: &[Range<Anchor>], cx: &mut ViewContext<Self>) {
let existing_range = self
.background_highlights
.get(&TypeId::of::<BufferSearchHighlights>())
.map(|(_, range)| range.as_ref());
let updated = existing_range != Some(matches);
self.highlight_background::<BufferSearchHighlights>(
matches,
|theme| theme.search_match_background,
cx,
);
if updated {
cx.emit(SearchEvent::MatchesInvalidated);
}
}
fn has_filtered_search_ranges(&mut self) -> bool {

View File

@ -26,9 +26,11 @@ log.workspace = true
menu.workspace = true
project.workspace = true
schemars.workspace = true
search.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
theme.workspace = true
util.workspace = true
worktree.workspace = true
workspace.workspace = true

File diff suppressed because it is too large Load Diff

View File

@ -988,7 +988,11 @@ impl BufferSearchBar {
.searchable_items_with_matches
.get(&active_searchable_item.downgrade())
.unwrap();
active_searchable_item.update_matches(matches, cx);
if matches.is_empty() {
active_searchable_item.clear_matches(cx);
} else {
active_searchable_item.update_matches(matches, cx);
}
let _ = done_tx.send(());
}
cx.notify();

View File

@ -503,6 +503,10 @@ impl Item for ProjectSearchView {
}
impl ProjectSearchView {
pub fn get_matches(&self, cx: &AppContext) -> Vec<Range<Anchor>> {
self.model.read(cx).match_ranges.clone()
}
fn toggle_filters(&mut self, cx: &mut ViewContext<Self>) {
self.filters_enabled = !self.filters_enabled;
ActiveSettings::update_global(cx, |settings, cx| {
@ -836,6 +840,10 @@ impl ProjectSearchView {
}
}
pub fn search_query_text(&self, cx: &WindowContext) -> String {
self.query_editor.read(cx).text(cx)
}
fn build_search_query(&mut self, cx: &mut ViewContext<Self>) -> Option<SearchQuery> {
// Do not bail early in this function, as we want to fill out `self.panels_with_errors`.
let text = self.query_editor.read(cx).text(cx);

View File

@ -212,6 +212,7 @@ pub enum IconName {
PageUp,
Pencil,
Person,
Pin,
Play,
Plus,
Public,
@ -263,6 +264,7 @@ pub enum IconName {
Trash,
TriangleRight,
Undo,
Unpin,
Update,
WholeWord,
XCircle,
@ -381,6 +383,7 @@ impl IconName {
IconName::PageUp => "icons/page_up.svg",
IconName::Pencil => "icons/pencil.svg",
IconName::Person => "icons/person.svg",
IconName::Pin => "icons/pin.svg",
IconName::Play => "icons/play.svg",
IconName::Plus => "icons/plus.svg",
IconName::Public => "icons/public.svg",
@ -431,6 +434,7 @@ impl IconName {
IconName::TextSelect => "icons/text_select.svg",
IconName::Trash => "icons/trash.svg",
IconName::TriangleRight => "icons/triangle_right.svg",
IconName::Unpin => "icons/unpin.svg",
IconName::Update => "icons/update.svg",
IconName::Undo => "icons/undo.svg",
IconName::WholeWord => "icons/word_search.svg",

View File

@ -65,6 +65,9 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
fn toggle_filtered_search_ranges(&mut self, _enabled: bool, _cx: &mut ViewContext<Self>) {}
fn get_matches(&self, _: &mut WindowContext) -> Vec<Self::Match> {
Vec::new()
}
fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
fn update_matches(&mut self, matches: &[Self::Match], cx: &mut ViewContext<Self>);
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;