Introduce an outline panel (#12637)
Adds a new panel: `OutlinePanel` which looks very close to project panel: <img width="256" alt="Screenshot 2024-06-10 at 23 19 05" src="https://github.com/zed-industries/zed/assets/2690773/c66e6e78-44ec-4de8-8d60-43238bb09ae9"> has similar settings and keymap (actions work in the `OutlinePanel` context and are under `outline_panel::` namespace), with two notable differences: * no "edit" actions such as cut/copy/paste/delete/etc. * directory auto folding is enabled by default Empty view: <img width="841" alt="Screenshot 2024-06-10 at 23 19 11" src="https://github.com/zed-industries/zed/assets/2690773/dc8bf37c-5a70-4fd5-9b57-76271eb7a40c"> When editor gets active, the panel displays all related files in a tree (similar to what the project panel does) and all related excerpts' outlines under each file. Same as in the project panel, directories can be expanded or collapsed, unfolded or folded; clicking file entries or outlines scrolls the buffer to the corresponding excerpt; changing editor's selection reveals the corresponding outline in the panel. The panel is applicable to any singleton buffer: <img width="1215" alt="Screenshot 2024-06-10 at 23 19 35" src="https://github.com/zed-industries/zed/assets/2690773/a087631f-5c2d-4d4d-ae25-30ab9731d528"> <img width="1728" alt="image" src="https://github.com/zed-industries/zed/assets/2690773/e4f8082c-d12d-4473-8500-e8fd1051285b"> or any multi buffer: (search multi buffer) <img width="1728" alt="Screenshot 2024-06-10 at 23 19 41" src="https://github.com/zed-industries/zed/assets/2690773/60f768a3-6716-4520-9b13-42da8fd15f50"> (diagnostics multi buffer) <img width="1728" alt="image" src="https://github.com/zed-industries/zed/assets/2690773/64e285bd-9530-4bf2-8f1f-10ee5596067c"> Release Notes: - Added an outline panel to show a "map" of the active editor
This commit is contained in:
parent
7f56f4e78e
commit
8451dba6a7
27 changed files with 2860 additions and 57 deletions
27
Cargo.lock
generated
27
Cargo.lock
generated
|
@ -7132,7 +7132,6 @@ dependencies = [
|
||||||
"project",
|
"project",
|
||||||
"rope",
|
"rope",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
|
||||||
"smol",
|
"smol",
|
||||||
"theme",
|
"theme",
|
||||||
"tree-sitter-rust",
|
"tree-sitter-rust",
|
||||||
|
@ -7142,6 +7141,31 @@ dependencies = [
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "outline_panel"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"collections",
|
||||||
|
"db",
|
||||||
|
"editor",
|
||||||
|
"file_icons",
|
||||||
|
"git",
|
||||||
|
"gpui",
|
||||||
|
"language",
|
||||||
|
"log",
|
||||||
|
"menu",
|
||||||
|
"project",
|
||||||
|
"schemars",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"settings",
|
||||||
|
"unicase",
|
||||||
|
"util",
|
||||||
|
"workspace",
|
||||||
|
"worktree",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "outref"
|
name = "outref"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -13255,6 +13279,7 @@ dependencies = [
|
||||||
"node_runtime",
|
"node_runtime",
|
||||||
"notifications",
|
"notifications",
|
||||||
"outline",
|
"outline",
|
||||||
|
"outline_panel",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"profiling",
|
"profiling",
|
||||||
"project",
|
"project",
|
||||||
|
|
|
@ -64,6 +64,7 @@ members = [
|
||||||
"crates/ollama",
|
"crates/ollama",
|
||||||
"crates/open_ai",
|
"crates/open_ai",
|
||||||
"crates/outline",
|
"crates/outline",
|
||||||
|
"crates/outline_panel",
|
||||||
"crates/picker",
|
"crates/picker",
|
||||||
"crates/prettier",
|
"crates/prettier",
|
||||||
"crates/project",
|
"crates/project",
|
||||||
|
@ -212,6 +213,7 @@ notifications = { path = "crates/notifications" }
|
||||||
ollama = { path = "crates/ollama" }
|
ollama = { path = "crates/ollama" }
|
||||||
open_ai = { path = "crates/open_ai" }
|
open_ai = { path = "crates/open_ai" }
|
||||||
outline = { path = "crates/outline" }
|
outline = { path = "crates/outline" }
|
||||||
|
outline_panel = { path = "crates/outline_panel" }
|
||||||
picker = { path = "crates/picker" }
|
picker = { path = "crates/picker" }
|
||||||
plugin = { path = "crates/plugin" }
|
plugin = { path = "crates/plugin" }
|
||||||
plugin_macros = { path = "crates/plugin_macros" }
|
plugin_macros = { path = "crates/plugin_macros" }
|
||||||
|
|
1
assets/icons/list_tree.svg
Normal file
1
assets/icons/list_tree.svg
Normal 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-list-tree"><path d="M21 12h-8"/><path d="M21 6H8"/><path d="M21 18h-8"/><path d="M3 6v4c0 1.1.9 2 2 2h3"/><path d="M3 10v6c0 1.1.9 2 2 2h3"/></svg>
|
After Width: | Height: | Size: 349 B |
|
@ -439,6 +439,7 @@
|
||||||
"ctrl-shift-p": "command_palette::Toggle",
|
"ctrl-shift-p": "command_palette::Toggle",
|
||||||
"ctrl-shift-m": "diagnostics::Deploy",
|
"ctrl-shift-m": "diagnostics::Deploy",
|
||||||
"ctrl-shift-e": "project_panel::ToggleFocus",
|
"ctrl-shift-e": "project_panel::ToggleFocus",
|
||||||
|
"ctrl-shift-b": "outline_panel::ToggleFocus",
|
||||||
"ctrl-?": "assistant::ToggleFocus",
|
"ctrl-?": "assistant::ToggleFocus",
|
||||||
"ctrl-alt-s": "workspace::SaveAll",
|
"ctrl-alt-s": "workspace::SaveAll",
|
||||||
"ctrl-k m": "language_selector::Toggle",
|
"ctrl-k m": "language_selector::Toggle",
|
||||||
|
@ -562,6 +563,18 @@
|
||||||
"ctrl-enter": "project_search::SearchInNew"
|
"ctrl-enter": "project_search::SearchInNew"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "OutlinePanel",
|
||||||
|
"bindings": {
|
||||||
|
"left": "project_panel::CollapseSelectedEntry",
|
||||||
|
"right": "project_panel::ExpandSelectedEntry",
|
||||||
|
"ctrl-alt-c": "project_panel::CopyPath",
|
||||||
|
"alt-ctrl-shift-c": "project_panel::CopyRelativePath",
|
||||||
|
"alt-ctrl-r": "project_panel::RevealInFinder",
|
||||||
|
"shift-down": "menu::SelectNext",
|
||||||
|
"shift-up": "menu::SelectPrev"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "ProjectPanel",
|
"context": "ProjectPanel",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
@ -583,7 +596,10 @@
|
||||||
"ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }],
|
"ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }],
|
||||||
"ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
"ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||||
"alt-ctrl-r": "project_panel::RevealInFinder",
|
"alt-ctrl-r": "project_panel::RevealInFinder",
|
||||||
"alt-shift-f": "project_panel::NewSearchInDirectory"
|
"alt-shift-f": "project_panel::NewSearchInDirectory",
|
||||||
|
"shift-down": "menu::SelectNext",
|
||||||
|
"shift-up": "menu::SelectPrev",
|
||||||
|
"escape": "menu::Cancel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -475,6 +475,7 @@
|
||||||
"cmd-shift-p": "command_palette::Toggle",
|
"cmd-shift-p": "command_palette::Toggle",
|
||||||
"cmd-shift-m": "diagnostics::Deploy",
|
"cmd-shift-m": "diagnostics::Deploy",
|
||||||
"cmd-shift-e": "project_panel::ToggleFocus",
|
"cmd-shift-e": "project_panel::ToggleFocus",
|
||||||
|
"cmd-shift-b": "outline_panel::ToggleFocus",
|
||||||
"cmd-?": "assistant::ToggleFocus",
|
"cmd-?": "assistant::ToggleFocus",
|
||||||
"cmd-alt-s": "workspace::SaveAll",
|
"cmd-alt-s": "workspace::SaveAll",
|
||||||
"cmd-k m": "language_selector::Toggle",
|
"cmd-k m": "language_selector::Toggle",
|
||||||
|
@ -584,6 +585,18 @@
|
||||||
"cmd-enter": "project_search::SearchInNew"
|
"cmd-enter": "project_search::SearchInNew"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "OutlinePanel",
|
||||||
|
"bindings": {
|
||||||
|
"left": "outline_panel::CollapseSelectedEntry",
|
||||||
|
"right": "outline_panel::ExpandSelectedEntry",
|
||||||
|
"cmd-alt-c": "outline_panel::CopyPath",
|
||||||
|
"alt-cmd-shift-c": "outline_panel::CopyRelativePath",
|
||||||
|
"alt-cmd-r": "outline_panel::RevealInFinder",
|
||||||
|
"shift-down": "menu::SelectNext",
|
||||||
|
"shift-up": "menu::SelectPrev"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "ProjectPanel",
|
"context": "ProjectPanel",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
|
|
@ -302,6 +302,29 @@
|
||||||
/// when a directory has only one directory inside.
|
/// when a directory has only one directory inside.
|
||||||
"auto_fold_dirs": false
|
"auto_fold_dirs": false
|
||||||
},
|
},
|
||||||
|
"outline_panel": {
|
||||||
|
// Whether to show the outline panel button in the status bar
|
||||||
|
"button": true,
|
||||||
|
// Default width of the outline panel.
|
||||||
|
"default_width": 240,
|
||||||
|
// Where to dock the outline panel. Can be 'left' or 'right'.
|
||||||
|
"dock": "left",
|
||||||
|
// Whether to show file icons in the outline panel.
|
||||||
|
"file_icons": true,
|
||||||
|
// Whether to show folder icons or chevrons for directories in the outline panel.
|
||||||
|
"folder_icons": true,
|
||||||
|
// Whether to show the git status in the outline panel.
|
||||||
|
"git_status": true,
|
||||||
|
// Amount of indentation for nested items.
|
||||||
|
"indent_size": 20,
|
||||||
|
// Whether to reveal it in the outline panel automatically,
|
||||||
|
// when a corresponding outline entry becomes active.
|
||||||
|
// Gitignored entries are never auto revealed.
|
||||||
|
"auto_reveal_entries": true,
|
||||||
|
/// Whether to fold directories automatically
|
||||||
|
/// when a directory has only one directory inside.
|
||||||
|
"auto_fold_dirs": true
|
||||||
|
},
|
||||||
"collaboration_panel": {
|
"collaboration_panel": {
|
||||||
// Whether to show the collaboration panel button in the status bar.
|
// Whether to show the collaboration panel button in the status bar.
|
||||||
"button": true,
|
"button": true,
|
||||||
|
|
|
@ -149,6 +149,9 @@ use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
|
||||||
|
|
||||||
use crate::hover_links::find_url;
|
use crate::hover_links::find_url;
|
||||||
|
|
||||||
|
pub const FILE_HEADER_HEIGHT: u8 = 1;
|
||||||
|
pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u8 = 1;
|
||||||
|
pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u8 = 1;
|
||||||
pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
|
pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
|
||||||
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
const MAX_LINE_LEN: usize = 1024;
|
const MAX_LINE_LEN: usize = 1024;
|
||||||
|
@ -529,6 +532,7 @@ pub struct Editor {
|
||||||
tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
|
tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
|
||||||
tasks_update_task: Option<Task<()>>,
|
tasks_update_task: Option<Task<()>>,
|
||||||
previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
|
previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
|
||||||
|
file_header_size: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -1651,9 +1655,8 @@ impl Editor {
|
||||||
}),
|
}),
|
||||||
merge_adjacent: true,
|
merge_adjacent: true,
|
||||||
};
|
};
|
||||||
|
let file_header_size = if show_excerpt_controls { 3 } else { 2 };
|
||||||
let display_map = cx.new_model(|cx| {
|
let display_map = cx.new_model(|cx| {
|
||||||
let file_header_size = if show_excerpt_controls { 3 } else { 2 };
|
|
||||||
|
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
style.font(),
|
style.font(),
|
||||||
|
@ -1661,8 +1664,8 @@ impl Editor {
|
||||||
None,
|
None,
|
||||||
show_excerpt_controls,
|
show_excerpt_controls,
|
||||||
file_header_size,
|
file_header_size,
|
||||||
1,
|
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
||||||
1,
|
MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
|
||||||
fold_placeholder,
|
fold_placeholder,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -1812,6 +1815,7 @@ impl Editor {
|
||||||
git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
|
git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
|
||||||
blame: None,
|
blame: None,
|
||||||
blame_subscription: None,
|
blame_subscription: None,
|
||||||
|
file_header_size,
|
||||||
tasks: Default::default(),
|
tasks: Default::default(),
|
||||||
_subscriptions: vec![
|
_subscriptions: vec![
|
||||||
cx.observe(&buffer, Self::on_buffer_changed),
|
cx.observe(&buffer, Self::on_buffer_changed),
|
||||||
|
@ -10829,6 +10833,12 @@ impl Editor {
|
||||||
self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
|
self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
|
||||||
cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
|
cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
|
||||||
}
|
}
|
||||||
|
multi_buffer::Event::ExcerptsEdited { ids } => {
|
||||||
|
cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
|
||||||
|
}
|
||||||
|
multi_buffer::Event::ExcerptsExpanded { ids } => {
|
||||||
|
cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
|
||||||
|
}
|
||||||
multi_buffer::Event::Reparsed => {
|
multi_buffer::Event::Reparsed => {
|
||||||
self.tasks_update_task = Some(self.refresh_runnables(cx));
|
self.tasks_update_task = Some(self.refresh_runnables(cx));
|
||||||
|
|
||||||
|
@ -11299,6 +11309,10 @@ impl Editor {
|
||||||
}));
|
}));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file_header_size(&self) -> u8 {
|
||||||
|
self.file_header_size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hunks_for_selections(
|
fn hunks_for_selections(
|
||||||
|
@ -11743,6 +11757,12 @@ pub enum EditorEvent {
|
||||||
ExcerptsRemoved {
|
ExcerptsRemoved {
|
||||||
ids: Vec<ExcerptId>,
|
ids: Vec<ExcerptId>,
|
||||||
},
|
},
|
||||||
|
ExcerptsEdited {
|
||||||
|
ids: Vec<ExcerptId>,
|
||||||
|
},
|
||||||
|
ExcerptsExpanded {
|
||||||
|
ids: Vec<ExcerptId>,
|
||||||
|
},
|
||||||
BufferEdited,
|
BufferEdited,
|
||||||
Edited,
|
Edited,
|
||||||
Reparsed,
|
Reparsed,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
pub struct CharBag(u64);
|
pub struct CharBag(u64);
|
||||||
|
|
||||||
impl CharBag {
|
impl CharBag {
|
||||||
|
|
|
@ -316,7 +316,7 @@ fn check_path_to_repo_path_errors(relative_file_path: &Path) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum GitFileStatus {
|
pub enum GitFileStatus {
|
||||||
Added,
|
Added,
|
||||||
Modified,
|
Modified,
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use anyhow::{bail, Context};
|
use anyhow::{bail, Context};
|
||||||
use serde::de::{self, Deserialize, Deserializer, Visitor};
|
use serde::de::{self, Deserialize, Deserializer, Visitor};
|
||||||
use std::fmt;
|
use std::{
|
||||||
|
fmt,
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
};
|
||||||
|
|
||||||
/// Convert an RGB hex color code number to a color type
|
/// Convert an RGB hex color code number to a color type
|
||||||
pub fn rgb(hex: u32) -> Rgba {
|
pub fn rgb(hex: u32) -> Rgba {
|
||||||
|
@ -267,6 +270,15 @@ impl Ord for Hsla {
|
||||||
|
|
||||||
impl Eq for Hsla {}
|
impl Eq for Hsla {}
|
||||||
|
|
||||||
|
impl Hash for Hsla {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
state.write_u32(u32::from_be_bytes(self.h.to_be_bytes()));
|
||||||
|
state.write_u32(u32::from_be_bytes(self.s.to_be_bytes()));
|
||||||
|
state.write_u32(u32::from_be_bytes(self.l.to_be_bytes()));
|
||||||
|
state.write_u32(u32::from_be_bytes(self.a.to_be_bytes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct an [`Hsla`] object from plain values
|
/// Construct an [`Hsla`] object from plain values
|
||||||
pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
|
pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
|
||||||
Hsla {
|
Hsla {
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use std::{iter, mem, ops::Range};
|
use std::{
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
iter, mem,
|
||||||
|
ops::Range,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
|
black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
|
||||||
|
@ -319,6 +323,20 @@ pub struct HighlightStyle {
|
||||||
|
|
||||||
impl Eq for HighlightStyle {}
|
impl Eq for HighlightStyle {}
|
||||||
|
|
||||||
|
impl Hash for HighlightStyle {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.color.hash(state);
|
||||||
|
self.font_weight.hash(state);
|
||||||
|
self.font_style.hash(state);
|
||||||
|
self.background_color.hash(state);
|
||||||
|
self.underline.hash(state);
|
||||||
|
self.strikethrough.hash(state);
|
||||||
|
state.write_u32(u32::from_be_bytes(
|
||||||
|
self.fade_out.map(|f| f.to_be_bytes()).unwrap_or_default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Style {
|
impl Style {
|
||||||
/// Returns true if the style is visible and the background is opaque.
|
/// Returns true if the style is visible and the background is opaque.
|
||||||
pub fn has_opaque_background(&self) -> bool {
|
pub fn has_opaque_background(&self) -> bool {
|
||||||
|
@ -549,7 +567,7 @@ impl Default for Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The properties that can be applied to an underline.
|
/// The properties that can be applied to an underline.
|
||||||
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq)]
|
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
|
||||||
#[refineable(Debug)]
|
#[refineable(Debug)]
|
||||||
pub struct UnderlineStyle {
|
pub struct UnderlineStyle {
|
||||||
/// The thickness of the underline.
|
/// The thickness of the underline.
|
||||||
|
@ -563,7 +581,7 @@ pub struct UnderlineStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The properties that can be applied to a strikethrough.
|
/// The properties that can be applied to a strikethrough.
|
||||||
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq)]
|
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
|
||||||
#[refineable(Debug)]
|
#[refineable(Debug)]
|
||||||
pub struct StrikethroughStyle {
|
pub struct StrikethroughStyle {
|
||||||
/// The thickness of the strikethrough.
|
/// The thickness of the strikethrough.
|
||||||
|
|
|
@ -2738,12 +2738,13 @@ impl BufferSnapshot {
|
||||||
Some(items)
|
Some(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn outline_items_containing(
|
pub fn outline_items_containing<T: ToOffset>(
|
||||||
&self,
|
&self,
|
||||||
range: Range<usize>,
|
range: Range<T>,
|
||||||
include_extra_context: bool,
|
include_extra_context: bool,
|
||||||
theme: Option<&SyntaxTheme>,
|
theme: Option<&SyntaxTheme>,
|
||||||
) -> Option<Vec<OutlineItem<Anchor>>> {
|
) -> Option<Vec<OutlineItem<Anchor>>> {
|
||||||
|
let range = range.to_offset(self);
|
||||||
let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
|
let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
|
||||||
grammar.outline_config.as_ref().map(|c| &c.query)
|
grammar.outline_config.as_ref().map(|c| &c.query)
|
||||||
});
|
});
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub use language_registry::{
|
||||||
PendingLanguageServer, QUERY_FILENAME_PREFIXES,
|
PendingLanguageServer, QUERY_FILENAME_PREFIXES,
|
||||||
};
|
};
|
||||||
pub use lsp::LanguageServerId;
|
pub use lsp::LanguageServerId;
|
||||||
pub use outline::{Outline, OutlineItem};
|
pub use outline::{render_item, Outline, OutlineItem};
|
||||||
pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};
|
pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};
|
||||||
pub use text::{AnchorRangeExt, LineEnding};
|
pub use text::{AnchorRangeExt, LineEnding};
|
||||||
pub use tree_sitter::{Node, Parser, Tree, TreeCursor};
|
pub use tree_sitter::{Node, Parser, Tree, TreeCursor};
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{BackgroundExecutor, HighlightStyle};
|
use gpui::{
|
||||||
|
relative, AppContext, BackgroundExecutor, FontStyle, FontWeight, HighlightStyle, StyledText,
|
||||||
|
TextStyle, WhiteSpace,
|
||||||
|
};
|
||||||
|
use settings::Settings;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
use theme::{ActiveTheme, ThemeSettings};
|
||||||
|
|
||||||
/// An outline of all the symbols contained in a buffer.
|
/// An outline of all the symbols contained in a buffer.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -11,7 +16,7 @@ pub struct Outline<T> {
|
||||||
path_candidate_prefixes: Vec<usize>,
|
path_candidate_prefixes: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct OutlineItem<T> {
|
pub struct OutlineItem<T> {
|
||||||
pub depth: usize,
|
pub depth: usize,
|
||||||
pub range: Range<T>,
|
pub range: Range<T>,
|
||||||
|
@ -138,3 +143,34 @@ impl<T> Outline<T> {
|
||||||
tree_matches
|
tree_matches
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render_item<T>(
|
||||||
|
outline_item: &OutlineItem<T>,
|
||||||
|
custom_highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> StyledText {
|
||||||
|
let settings = ThemeSettings::get_global(cx);
|
||||||
|
|
||||||
|
// TODO: We probably shouldn't need to build a whole new text style here
|
||||||
|
// but I'm not sure how to get the current one and modify it.
|
||||||
|
// Before this change TextStyle::default() was used here, which was giving us the wrong font and text color.
|
||||||
|
let text_style = TextStyle {
|
||||||
|
color: cx.theme().colors().text,
|
||||||
|
font_family: settings.buffer_font.family.clone(),
|
||||||
|
font_features: settings.buffer_font.features.clone(),
|
||||||
|
font_size: settings.buffer_font_size(cx).into(),
|
||||||
|
font_weight: FontWeight::NORMAL,
|
||||||
|
font_style: FontStyle::Normal,
|
||||||
|
line_height: relative(1.),
|
||||||
|
background_color: None,
|
||||||
|
underline: None,
|
||||||
|
strikethrough: None,
|
||||||
|
white_space: WhiteSpace::Normal,
|
||||||
|
};
|
||||||
|
let highlights = gpui::combine_highlights(
|
||||||
|
custom_highlights,
|
||||||
|
outline_item.highlight_ranges.iter().cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
StyledText::new(outline_item.text.clone()).with_highlights(&text_style, highlights)
|
||||||
|
}
|
||||||
|
|
|
@ -77,6 +77,9 @@ pub enum Event {
|
||||||
ExcerptsRemoved {
|
ExcerptsRemoved {
|
||||||
ids: Vec<ExcerptId>,
|
ids: Vec<ExcerptId>,
|
||||||
},
|
},
|
||||||
|
ExcerptsExpanded {
|
||||||
|
ids: Vec<ExcerptId>,
|
||||||
|
},
|
||||||
ExcerptsEdited {
|
ExcerptsEdited {
|
||||||
ids: Vec<ExcerptId>,
|
ids: Vec<ExcerptId>,
|
||||||
},
|
},
|
||||||
|
@ -1666,8 +1669,9 @@ impl MultiBuffer {
|
||||||
}
|
}
|
||||||
self.sync(cx);
|
self.sync(cx);
|
||||||
|
|
||||||
|
let ids = ids.into_iter().collect::<Vec<_>>();
|
||||||
let snapshot = self.snapshot(cx);
|
let snapshot = self.snapshot(cx);
|
||||||
let locators = snapshot.excerpt_locators_for_ids(ids);
|
let locators = snapshot.excerpt_locators_for_ids(ids.iter().copied());
|
||||||
let mut new_excerpts = SumTree::new();
|
let mut new_excerpts = SumTree::new();
|
||||||
let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>();
|
let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>();
|
||||||
let mut edits = Vec::<Edit<usize>>::new();
|
let mut edits = Vec::<Edit<usize>>::new();
|
||||||
|
@ -1746,6 +1750,7 @@ impl MultiBuffer {
|
||||||
cx.emit(Event::Edited {
|
cx.emit(Event::Edited {
|
||||||
singleton_buffer_edited: false,
|
singleton_buffer_edited: false,
|
||||||
});
|
});
|
||||||
|
cx.emit(Event::ExcerptsExpanded { ids });
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ gpui.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
ordered-float.workspace = true
|
ordered-float.workspace = true
|
||||||
picker.workspace = true
|
picker.workspace = true
|
||||||
settings.workspace = true
|
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
|
|
|
@ -2,19 +2,18 @@ use editor::{scroll::Autoscroll, Anchor, AnchorRangeExt, Editor, EditorMode};
|
||||||
use fuzzy::StringMatch;
|
use fuzzy::StringMatch;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
|
actions, div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
|
||||||
FontStyle, FontWeight, HighlightStyle, ParentElement, Point, Render, Styled, StyledText, Task,
|
HighlightStyle, ParentElement, Point, Render, Styled, Task, View, ViewContext, VisualContext,
|
||||||
TextStyle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
|
WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use language::Outline;
|
use language::Outline;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use settings::Settings;
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Reverse},
|
cmp::{self, Reverse},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use theme::{color_alpha, ActiveTheme, ThemeSettings};
|
use theme::{color_alpha, ActiveTheme};
|
||||||
use ui::{prelude::*, ListItem, ListItemSpacing};
|
use ui::{prelude::*, ListItem, ListItemSpacing};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{DismissDecision, ModalView};
|
use workspace::{DismissDecision, ModalView};
|
||||||
|
@ -268,38 +267,12 @@ impl PickerDelegate for OutlineViewDelegate {
|
||||||
selected: bool,
|
selected: bool,
|
||||||
cx: &mut ViewContext<Picker<Self>>,
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
) -> Option<Self::ListItem> {
|
) -> Option<Self::ListItem> {
|
||||||
let settings = ThemeSettings::get_global(cx);
|
let mat = self.matches.get(ix)?;
|
||||||
|
let outline_item = self.outline.items.get(mat.candidate_id)?;
|
||||||
// TODO: We probably shouldn't need to build a whole new text style here
|
|
||||||
// but I'm not sure how to get the current one and modify it.
|
|
||||||
// Before this change TextStyle::default() was used here, which was giving us the wrong font and text color.
|
|
||||||
let text_style = TextStyle {
|
|
||||||
color: cx.theme().colors().text,
|
|
||||||
font_family: settings.buffer_font.family.clone(),
|
|
||||||
font_features: settings.buffer_font.features.clone(),
|
|
||||||
font_size: settings.buffer_font_size(cx).into(),
|
|
||||||
font_weight: FontWeight::NORMAL,
|
|
||||||
font_style: FontStyle::Normal,
|
|
||||||
line_height: relative(1.),
|
|
||||||
background_color: None,
|
|
||||||
underline: None,
|
|
||||||
strikethrough: None,
|
|
||||||
white_space: WhiteSpace::Normal,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut highlight_style = HighlightStyle::default();
|
let mut highlight_style = HighlightStyle::default();
|
||||||
highlight_style.background_color = Some(color_alpha(cx.theme().colors().text_accent, 0.3));
|
highlight_style.background_color = Some(color_alpha(cx.theme().colors().text_accent, 0.3));
|
||||||
|
let custom_highlights = mat.ranges().map(|range| (range, highlight_style));
|
||||||
let mat = &self.matches[ix];
|
|
||||||
let outline_item = &self.outline.items[mat.candidate_id];
|
|
||||||
|
|
||||||
let highlights = gpui::combine_highlights(
|
|
||||||
mat.ranges().map(|range| (range, highlight_style)),
|
|
||||||
outline_item.highlight_ranges.iter().cloned(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let styled_text =
|
|
||||||
StyledText::new(outline_item.text.clone()).with_highlights(&text_style, highlights);
|
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
ListItem::new(ix)
|
ListItem::new(ix)
|
||||||
|
@ -310,7 +283,7 @@ impl PickerDelegate for OutlineViewDelegate {
|
||||||
div()
|
div()
|
||||||
.text_ui(cx)
|
.text_ui(cx)
|
||||||
.pl(rems(outline_item.depth as f32))
|
.pl(rems(outline_item.depth as f32))
|
||||||
.child(styled_text),
|
.child(language::render_item(outline_item, custom_highlights, cx)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
37
crates/outline_panel/Cargo.toml
Normal file
37
crates/outline_panel/Cargo.toml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[package]
|
||||||
|
name = "outline_panel"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/outline_panel.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
|
collections.workspace = true
|
||||||
|
db.workspace = true
|
||||||
|
editor.workspace = true
|
||||||
|
file_icons.workspace = true
|
||||||
|
git.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
language.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
menu.workspace = true
|
||||||
|
project.workspace = true
|
||||||
|
schemars.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
settings.workspace = true
|
||||||
|
unicase.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
worktree.workspace = true
|
||||||
|
workspace.workspace = true
|
||||||
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = ["log"]
|
1
crates/outline_panel/LICENSE-GPL
Symbolic link
1
crates/outline_panel/LICENSE-GPL
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-GPL
|
2515
crates/outline_panel/src/outline_panel.rs
Normal file
2515
crates/outline_panel/src/outline_panel.rs
Normal file
File diff suppressed because it is too large
Load diff
81
crates/outline_panel/src/outline_panel_settings.rs
Normal file
81
crates/outline_panel/src/outline_panel_settings.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
use anyhow;
|
||||||
|
use gpui::Pixels;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use settings::{Settings, SettingsSources};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum OutlinePanelDockPosition {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct OutlinePanelSettings {
|
||||||
|
pub button: bool,
|
||||||
|
pub default_width: Pixels,
|
||||||
|
pub dock: OutlinePanelDockPosition,
|
||||||
|
pub file_icons: bool,
|
||||||
|
pub folder_icons: bool,
|
||||||
|
pub git_status: bool,
|
||||||
|
pub indent_size: f32,
|
||||||
|
pub auto_reveal_entries: bool,
|
||||||
|
pub auto_fold_dirs: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct OutlinePanelSettingsContent {
|
||||||
|
/// Whether to show the outline panel button in the status bar.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub button: Option<bool>,
|
||||||
|
/// Customise default width (in pixels) taken by outline panel
|
||||||
|
///
|
||||||
|
/// Default: 240
|
||||||
|
pub default_width: Option<f32>,
|
||||||
|
/// The position of outline panel
|
||||||
|
///
|
||||||
|
/// Default: left
|
||||||
|
pub dock: Option<OutlinePanelDockPosition>,
|
||||||
|
/// Whether to show file icons in the outline panel.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub file_icons: Option<bool>,
|
||||||
|
/// Whether to show folder icons or chevrons for directories in the outline panel.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub folder_icons: Option<bool>,
|
||||||
|
/// Whether to show the git status in the outline panel.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub git_status: Option<bool>,
|
||||||
|
/// Amount of indentation (in pixels) for nested items.
|
||||||
|
///
|
||||||
|
/// Default: 20
|
||||||
|
pub indent_size: Option<f32>,
|
||||||
|
/// Whether to reveal it in the outline panel automatically,
|
||||||
|
/// when a corresponding project entry becomes active.
|
||||||
|
/// Gitignored entries are never auto revealed.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub auto_reveal_entries: Option<bool>,
|
||||||
|
/// Whether to fold directories automatically
|
||||||
|
/// when directory has only one directory inside.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub auto_fold_dirs: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings for OutlinePanelSettings {
|
||||||
|
const KEY: Option<&'static str> = Some("outline_panel");
|
||||||
|
|
||||||
|
type FileContent = OutlinePanelSettingsContent;
|
||||||
|
|
||||||
|
fn load(
|
||||||
|
sources: SettingsSources<Self::FileContent>,
|
||||||
|
_: &mut gpui::AppContext,
|
||||||
|
) -> anyhow::Result<Self> {
|
||||||
|
sources.json_merge()
|
||||||
|
}
|
||||||
|
}
|
|
@ -144,6 +144,7 @@ pub enum IconName {
|
||||||
InlayHint,
|
InlayHint,
|
||||||
Library,
|
Library,
|
||||||
Link,
|
Link,
|
||||||
|
ListTree,
|
||||||
MagicWand,
|
MagicWand,
|
||||||
MagnifyingGlass,
|
MagnifyingGlass,
|
||||||
MailOpen,
|
MailOpen,
|
||||||
|
@ -274,6 +275,7 @@ impl IconName {
|
||||||
IconName::InlayHint => "icons/inlay_hint.svg",
|
IconName::InlayHint => "icons/inlay_hint.svg",
|
||||||
IconName::Library => "icons/library.svg",
|
IconName::Library => "icons/library.svg",
|
||||||
IconName::Link => "icons/link.svg",
|
IconName::Link => "icons/link.svg",
|
||||||
|
IconName::ListTree => "icons/list_tree.svg",
|
||||||
IconName::MagicWand => "icons/magic_wand.svg",
|
IconName::MagicWand => "icons/magic_wand.svg",
|
||||||
IconName::MagnifyingGlass => "icons/magnifying_glass.svg",
|
IconName::MagnifyingGlass => "icons/magnifying_glass.svg",
|
||||||
IconName::MailOpen => "icons/mail_open.svg",
|
IconName::MailOpen => "icons/mail_open.svg",
|
||||||
|
|
|
@ -2000,7 +2000,7 @@ impl Snapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse_from_path(
|
pub fn traverse_from_path(
|
||||||
&self,
|
&self,
|
||||||
include_files: bool,
|
include_files: bool,
|
||||||
include_dirs: bool,
|
include_dirs: bool,
|
||||||
|
@ -2991,7 +2991,7 @@ impl File {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Entry {
|
pub struct Entry {
|
||||||
pub id: ProjectEntryId,
|
pub id: ProjectEntryId,
|
||||||
pub kind: EntryKind,
|
pub kind: EntryKind,
|
||||||
|
@ -3020,7 +3020,7 @@ pub struct Entry {
|
||||||
pub is_private: bool,
|
pub is_private: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum EntryKind {
|
pub enum EntryKind {
|
||||||
UnloadedDir,
|
UnloadedDir,
|
||||||
PendingDir,
|
PendingDir,
|
||||||
|
@ -4818,6 +4818,14 @@ impl<'a> Traversal<'a> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn back_to_parent(&mut self) -> bool {
|
||||||
|
let Some(parent_path) = self.cursor.item().and_then(|entry| entry.path.parent()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
self.cursor
|
||||||
|
.seek(&TraversalTarget::Path(parent_path), Bias::Left, &())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn entry(&self) -> Option<&'a Entry> {
|
pub fn entry(&self) -> Option<&'a Entry> {
|
||||||
self.cursor.item()
|
self.cursor.item()
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ nix = {workspace = true, features = ["pthread", "signal"] }
|
||||||
node_runtime.workspace = true
|
node_runtime.workspace = true
|
||||||
notifications.workspace = true
|
notifications.workspace = true
|
||||||
outline.workspace = true
|
outline.workspace = true
|
||||||
|
outline_panel.workspace = true
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
profiling.workspace = true
|
profiling.workspace = true
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
|
|
|
@ -185,6 +185,7 @@ fn init_ui(app_state: Arc<AppState>, cx: &mut AppContext) -> Result<()> {
|
||||||
outline::init(cx);
|
outline::init(cx);
|
||||||
project_symbols::init(cx);
|
project_symbols::init(cx);
|
||||||
project_panel::init(Assets, cx);
|
project_panel::init(Assets, cx);
|
||||||
|
outline_panel::init(Assets, cx);
|
||||||
tasks_ui::init(cx);
|
tasks_ui::init(cx);
|
||||||
channel::init(&app_state.client.clone(), app_state.user_store.clone(), cx);
|
channel::init(&app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||||
search::init(cx);
|
search::init(cx);
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub use open_listener::*;
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use assets::Assets;
|
use assets::Assets;
|
||||||
use futures::{channel::mpsc, select_biased, StreamExt};
|
use futures::{channel::mpsc, select_biased, StreamExt};
|
||||||
|
use outline_panel::OutlinePanel;
|
||||||
use project::TaskSourceKind;
|
use project::TaskSourceKind;
|
||||||
use project_panel::ProjectPanel;
|
use project_panel::ProjectPanel;
|
||||||
use quick_action_bar::QuickActionBar;
|
use quick_action_bar::QuickActionBar;
|
||||||
|
@ -190,6 +191,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
let assistant_panel =
|
let assistant_panel =
|
||||||
assistant::AssistantPanel::load(workspace_handle.clone(), cx.clone());
|
assistant::AssistantPanel::load(workspace_handle.clone(), cx.clone());
|
||||||
let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
|
let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
|
||||||
|
let outline_panel = OutlinePanel::load(workspace_handle.clone(), cx.clone());
|
||||||
let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
|
let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
|
||||||
let channels_panel =
|
let channels_panel =
|
||||||
collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone());
|
collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone());
|
||||||
|
@ -202,6 +204,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
|
|
||||||
let (
|
let (
|
||||||
project_panel,
|
project_panel,
|
||||||
|
outline_panel,
|
||||||
terminal_panel,
|
terminal_panel,
|
||||||
assistant_panel,
|
assistant_panel,
|
||||||
channels_panel,
|
channels_panel,
|
||||||
|
@ -209,6 +212,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
notification_panel,
|
notification_panel,
|
||||||
) = futures::try_join!(
|
) = futures::try_join!(
|
||||||
project_panel,
|
project_panel,
|
||||||
|
outline_panel,
|
||||||
terminal_panel,
|
terminal_panel,
|
||||||
assistant_panel,
|
assistant_panel,
|
||||||
channels_panel,
|
channels_panel,
|
||||||
|
@ -219,6 +223,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
workspace_handle.update(&mut cx, |workspace, cx| {
|
workspace_handle.update(&mut cx, |workspace, cx| {
|
||||||
workspace.add_panel(assistant_panel, cx);
|
workspace.add_panel(assistant_panel, cx);
|
||||||
workspace.add_panel(project_panel, cx);
|
workspace.add_panel(project_panel, cx);
|
||||||
|
workspace.add_panel(outline_panel, cx);
|
||||||
workspace.add_panel(terminal_panel, cx);
|
workspace.add_panel(terminal_panel, cx);
|
||||||
workspace.add_panel(channels_panel, cx);
|
workspace.add_panel(channels_panel, cx);
|
||||||
workspace.add_panel(chat_panel, cx);
|
workspace.add_panel(chat_panel, cx);
|
||||||
|
@ -377,6 +382,13 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
workspace.toggle_panel_focus::<ProjectPanel>(cx);
|
workspace.toggle_panel_focus::<ProjectPanel>(cx);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.register_action(
|
||||||
|
|workspace: &mut Workspace,
|
||||||
|
_: &outline_panel::ToggleFocus,
|
||||||
|
cx: &mut ViewContext<Workspace>| {
|
||||||
|
workspace.toggle_panel_focus::<OutlinePanel>(cx);
|
||||||
|
},
|
||||||
|
)
|
||||||
.register_action(
|
.register_action(
|
||||||
|workspace: &mut Workspace,
|
|workspace: &mut Workspace,
|
||||||
_: &collab_ui::collab_panel::ToggleFocus,
|
_: &collab_ui::collab_panel::ToggleFocus,
|
||||||
|
@ -3093,9 +3105,9 @@ mod tests {
|
||||||
command_palette::init(cx);
|
command_palette::init(cx);
|
||||||
language::init(cx);
|
language::init(cx);
|
||||||
editor::init(cx);
|
editor::init(cx);
|
||||||
project_panel::init_settings(cx);
|
|
||||||
collab_ui::init(&app_state, cx);
|
collab_ui::init(&app_state, cx);
|
||||||
project_panel::init((), cx);
|
project_panel::init((), cx);
|
||||||
|
outline_panel::init((), cx);
|
||||||
terminal_view::init(cx);
|
terminal_view::init(cx);
|
||||||
assistant::init(app_state.client.clone(), cx);
|
assistant::init(app_state.client.clone(), cx);
|
||||||
tasks_ui::init(cx);
|
tasks_ui::init(cx);
|
||||||
|
|
|
@ -123,6 +123,7 @@ pub fn app_menus() -> Vec<Menu<'static>> {
|
||||||
}),
|
}),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::action("Project Panel", project_panel::ToggleFocus),
|
MenuItem::action("Project Panel", project_panel::ToggleFocus),
|
||||||
|
MenuItem::action("Outline Panel", outline_panel::ToggleFocus),
|
||||||
MenuItem::action("Collab Panel", collab_panel::ToggleFocus),
|
MenuItem::action("Collab Panel", collab_panel::ToggleFocus),
|
||||||
MenuItem::action("Terminal Panel", terminal_panel::ToggleFocus),
|
MenuItem::action("Terminal Panel", terminal_panel::ToggleFocus),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue