Merge branch 'main' into copilot
This commit is contained in:
commit
ae3b3ea458
20 changed files with 351 additions and 165 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -785,6 +785,7 @@ dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"itertools",
|
"itertools",
|
||||||
"language",
|
"language",
|
||||||
|
"outline",
|
||||||
"project",
|
"project",
|
||||||
"search",
|
"search",
|
||||||
"settings",
|
"settings",
|
||||||
|
@ -8484,7 +8485,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.80.0"
|
version = "0.81.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activity_indicator",
|
"activity_indicator",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
|
@ -18,6 +18,7 @@ search = { path = "../search" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
outline = { path = "../outline" }
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*, AppContext, Entity, RenderContext, Subscription, View, ViewContext, ViewHandle,
|
elements::*, AppContext, Entity, MouseButton, RenderContext, Subscription, View, ViewContext,
|
||||||
|
ViewHandle,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use search::ProjectSearchView;
|
use search::ProjectSearchView;
|
||||||
|
@ -14,6 +15,7 @@ pub enum Event {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Breadcrumbs {
|
pub struct Breadcrumbs {
|
||||||
|
pane_focused: bool,
|
||||||
active_item: Option<Box<dyn ItemHandle>>,
|
active_item: Option<Box<dyn ItemHandle>>,
|
||||||
project_search: Option<ViewHandle<ProjectSearchView>>,
|
project_search: Option<ViewHandle<ProjectSearchView>>,
|
||||||
subscription: Option<Subscription>,
|
subscription: Option<Subscription>,
|
||||||
|
@ -22,6 +24,7 @@ pub struct Breadcrumbs {
|
||||||
impl Breadcrumbs {
|
impl Breadcrumbs {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
pane_focused: false,
|
||||||
active_item: Default::default(),
|
active_item: Default::default(),
|
||||||
subscription: Default::default(),
|
subscription: Default::default(),
|
||||||
project_search: Default::default(),
|
project_search: Default::default(),
|
||||||
|
@ -39,24 +42,53 @@ impl View for Breadcrumbs {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||||
|
let active_item = match &self.active_item {
|
||||||
|
Some(active_item) => active_item,
|
||||||
|
None => return Empty::new().boxed(),
|
||||||
|
};
|
||||||
|
let not_editor = active_item.downcast::<editor::Editor>().is_none();
|
||||||
|
|
||||||
let theme = cx.global::<Settings>().theme.clone();
|
let theme = cx.global::<Settings>().theme.clone();
|
||||||
if let Some(breadcrumbs) = self
|
let style = &theme.workspace.breadcrumbs;
|
||||||
.active_item
|
|
||||||
.as_ref()
|
let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
|
||||||
.and_then(|item| item.breadcrumbs(&theme, cx))
|
Some(breadcrumbs) => breadcrumbs,
|
||||||
{
|
None => return Empty::new().boxed(),
|
||||||
Flex::row()
|
};
|
||||||
.with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || {
|
|
||||||
Label::new(" 〉 ", theme.breadcrumbs.text.clone()).boxed()
|
let crumbs = Flex::row()
|
||||||
}))
|
.with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || {
|
||||||
.contained()
|
Label::new(" 〉 ", style.default.text.clone()).boxed()
|
||||||
.with_style(theme.breadcrumbs.container)
|
}))
|
||||||
|
.constrained()
|
||||||
|
.with_height(theme.workspace.breadcrumb_height)
|
||||||
|
.contained();
|
||||||
|
|
||||||
|
if not_editor || !self.pane_focused {
|
||||||
|
return crumbs
|
||||||
|
.with_style(style.default.container)
|
||||||
.aligned()
|
.aligned()
|
||||||
.left()
|
.left()
|
||||||
.boxed()
|
.boxed();
|
||||||
} else {
|
|
||||||
Empty::new().boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseEventHandler::<Breadcrumbs>::new(0, cx, |state, _| {
|
||||||
|
let style = style.style_for(state, false);
|
||||||
|
crumbs.with_style(style.container).boxed()
|
||||||
|
})
|
||||||
|
.on_click(MouseButton::Left, |_, cx| {
|
||||||
|
cx.dispatch_action(outline::Toggle);
|
||||||
|
})
|
||||||
|
.with_tooltip::<Breadcrumbs, _>(
|
||||||
|
0,
|
||||||
|
"Show symbol outline".to_owned(),
|
||||||
|
Some(Box::new(outline::Toggle)),
|
||||||
|
theme.tooltip.clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,4 +135,8 @@ impl ToolbarItemView for Breadcrumbs {
|
||||||
current_location
|
current_location
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pane_focus_update(&mut self, pane_focused: bool, _: &mut gpui::MutableAppContext) {
|
||||||
|
self.pane_focused = pane_focused;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
|
||||||
pub use suggestion_map::Suggestion;
|
pub use suggestion_map::Suggestion;
|
||||||
use suggestion_map::SuggestionMap;
|
use suggestion_map::SuggestionMap;
|
||||||
use sum_tree::{Bias, TreeMap};
|
use sum_tree::{Bias, TreeMap};
|
||||||
use tab_map::{TabMap, TabSnapshot};
|
use tab_map::TabMap;
|
||||||
use wrap_map::WrapMap;
|
use wrap_map::WrapMap;
|
||||||
|
|
||||||
pub use block_map::{
|
pub use block_map::{
|
||||||
|
@ -643,25 +643,21 @@ impl DisplaySnapshot {
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.buffer_line_for_row(buffer_row)
|
.buffer_line_for_row(buffer_row)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let chars = buffer.chars_at(Point::new(range.start.row, 0));
|
|
||||||
|
|
||||||
|
let mut indent_size = 0;
|
||||||
let mut is_blank = false;
|
let mut is_blank = false;
|
||||||
let indent_size = TabSnapshot::expand_tabs(
|
for c in buffer.chars_at(Point::new(range.start.row, 0)) {
|
||||||
chars.take_while(|c| {
|
if c == ' ' || c == '\t' {
|
||||||
if *c == ' ' || *c == '\t' {
|
indent_size += 1;
|
||||||
true
|
} else {
|
||||||
} else {
|
if c == '\n' {
|
||||||
if *c == '\n' {
|
is_blank = true;
|
||||||
is_blank = true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}),
|
break;
|
||||||
buffer.line_len(buffer_row) as usize, // Never collapse
|
}
|
||||||
self.tab_snapshot.tab_size,
|
}
|
||||||
);
|
|
||||||
|
|
||||||
(indent_size as u32, is_blank)
|
(indent_size, is_blank)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_len(&self, row: u32) -> u32 {
|
pub fn line_len(&self, row: u32) -> u32 {
|
||||||
|
|
|
@ -208,6 +208,32 @@ impl SuggestionSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn line_len(&self, row: u32) -> u32 {
|
||||||
|
if let Some(suggestion) = &self.suggestion {
|
||||||
|
let suggestion_start = suggestion.position.to_point(&self.fold_snapshot).0;
|
||||||
|
let suggestion_end = suggestion_start + suggestion.text.max_point();
|
||||||
|
|
||||||
|
if row < suggestion_start.row {
|
||||||
|
self.fold_snapshot.line_len(row)
|
||||||
|
} else if row > suggestion_end.row {
|
||||||
|
self.fold_snapshot
|
||||||
|
.line_len(suggestion_start.row + (row - suggestion_end.row))
|
||||||
|
} else {
|
||||||
|
let mut result = suggestion.text.line_len(row - suggestion_start.row);
|
||||||
|
if row == suggestion_start.row {
|
||||||
|
result += suggestion_start.column;
|
||||||
|
}
|
||||||
|
if row == suggestion_end.row {
|
||||||
|
result +=
|
||||||
|
self.fold_snapshot.line_len(suggestion_start.row) - suggestion_start.column;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.fold_snapshot.line_len(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clip_point(&self, point: SuggestionPoint, bias: Bias) -> SuggestionPoint {
|
pub fn clip_point(&self, point: SuggestionPoint, bias: Bias) -> SuggestionPoint {
|
||||||
if let Some(suggestion) = self.suggestion.as_ref() {
|
if let Some(suggestion) = self.suggestion.as_ref() {
|
||||||
let suggestion_start = suggestion.position.to_point(&self.fold_snapshot).0;
|
let suggestion_start = suggestion.position.to_point(&self.fold_snapshot).0;
|
||||||
|
|
|
@ -9,6 +9,8 @@ use parking_lot::Mutex;
|
||||||
use std::{cmp, mem, num::NonZeroU32, ops::Range};
|
use std::{cmp, mem, num::NonZeroU32, ops::Range};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
|
|
||||||
|
const MAX_EXPANSION_COLUMN: u32 = 256;
|
||||||
|
|
||||||
pub struct TabMap(Mutex<TabSnapshot>);
|
pub struct TabMap(Mutex<TabSnapshot>);
|
||||||
|
|
||||||
impl TabMap {
|
impl TabMap {
|
||||||
|
@ -16,11 +18,18 @@ impl TabMap {
|
||||||
let snapshot = TabSnapshot {
|
let snapshot = TabSnapshot {
|
||||||
suggestion_snapshot: input,
|
suggestion_snapshot: input,
|
||||||
tab_size,
|
tab_size,
|
||||||
|
max_expansion_column: MAX_EXPANSION_COLUMN,
|
||||||
version: 0,
|
version: 0,
|
||||||
};
|
};
|
||||||
(Self(Mutex::new(snapshot.clone())), snapshot)
|
(Self(Mutex::new(snapshot.clone())), snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn set_max_expansion_column(&self, column: u32) -> TabSnapshot {
|
||||||
|
self.0.lock().max_expansion_column = column;
|
||||||
|
self.0.lock().clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sync(
|
pub fn sync(
|
||||||
&self,
|
&self,
|
||||||
suggestion_snapshot: SuggestionSnapshot,
|
suggestion_snapshot: SuggestionSnapshot,
|
||||||
|
@ -31,6 +40,7 @@ impl TabMap {
|
||||||
let mut new_snapshot = TabSnapshot {
|
let mut new_snapshot = TabSnapshot {
|
||||||
suggestion_snapshot,
|
suggestion_snapshot,
|
||||||
tab_size,
|
tab_size,
|
||||||
|
max_expansion_column: old_snapshot.max_expansion_column,
|
||||||
version: old_snapshot.version,
|
version: old_snapshot.version,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,28 +52,66 @@ impl TabMap {
|
||||||
let mut tab_edits = Vec::with_capacity(suggestion_edits.len());
|
let mut tab_edits = Vec::with_capacity(suggestion_edits.len());
|
||||||
|
|
||||||
if old_snapshot.tab_size == new_snapshot.tab_size {
|
if old_snapshot.tab_size == new_snapshot.tab_size {
|
||||||
|
// Expand each edit to include the next tab on the same line as the edit,
|
||||||
|
// and any subsequent tabs on that line that moved across the tab expansion
|
||||||
|
// boundary.
|
||||||
for suggestion_edit in &mut suggestion_edits {
|
for suggestion_edit in &mut suggestion_edits {
|
||||||
let mut delta = 0;
|
let old_end_column = old_snapshot
|
||||||
for chunk in old_snapshot.suggestion_snapshot.chunks(
|
.suggestion_snapshot
|
||||||
|
.to_point(suggestion_edit.old.end)
|
||||||
|
.column();
|
||||||
|
let new_end_column = new_snapshot
|
||||||
|
.suggestion_snapshot
|
||||||
|
.to_point(suggestion_edit.new.end)
|
||||||
|
.column();
|
||||||
|
|
||||||
|
let mut offset_from_edit = 0;
|
||||||
|
let mut first_tab_offset = None;
|
||||||
|
let mut last_tab_with_changed_expansion_offset = None;
|
||||||
|
'outer: for chunk in old_snapshot.suggestion_snapshot.chunks(
|
||||||
suggestion_edit.old.end..old_max_offset,
|
suggestion_edit.old.end..old_max_offset,
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
let patterns: &[_] = &['\t', '\n'];
|
for (ix, mat) in chunk.text.match_indices(&['\t', '\n']) {
|
||||||
if let Some(ix) = chunk.text.find(patterns) {
|
let offset_from_edit = offset_from_edit + (ix as u32);
|
||||||
if &chunk.text[ix..ix + 1] == "\t" {
|
match mat {
|
||||||
suggestion_edit.old.end.0 += delta + ix + 1;
|
"\t" => {
|
||||||
suggestion_edit.new.end.0 += delta + ix + 1;
|
if first_tab_offset.is_none() {
|
||||||
}
|
first_tab_offset = Some(offset_from_edit);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
let old_column = old_end_column + offset_from_edit;
|
||||||
|
let new_column = new_end_column + offset_from_edit;
|
||||||
|
let was_expanded = old_column < old_snapshot.max_expansion_column;
|
||||||
|
let is_expanded = new_column < new_snapshot.max_expansion_column;
|
||||||
|
if was_expanded != is_expanded {
|
||||||
|
last_tab_with_changed_expansion_offset = Some(offset_from_edit);
|
||||||
|
} else if !was_expanded && !is_expanded {
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"\n" => break 'outer,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delta += chunk.text.len();
|
offset_from_edit += chunk.text.len() as u32;
|
||||||
|
if old_end_column + offset_from_edit >= old_snapshot.max_expansion_column
|
||||||
|
&& new_end_column | offset_from_edit >= new_snapshot.max_expansion_column
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(offset) = last_tab_with_changed_expansion_offset.or(first_tab_offset) {
|
||||||
|
suggestion_edit.old.end.0 += offset as usize + 1;
|
||||||
|
suggestion_edit.new.end.0 += offset as usize + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Combine any edits that overlap due to the expansion.
|
||||||
let mut ix = 1;
|
let mut ix = 1;
|
||||||
while ix < suggestion_edits.len() {
|
while ix < suggestion_edits.len() {
|
||||||
let (prev_edits, next_edits) = suggestion_edits.split_at_mut(ix);
|
let (prev_edits, next_edits) = suggestion_edits.split_at_mut(ix);
|
||||||
|
@ -113,6 +161,7 @@ impl TabMap {
|
||||||
pub struct TabSnapshot {
|
pub struct TabSnapshot {
|
||||||
pub suggestion_snapshot: SuggestionSnapshot,
|
pub suggestion_snapshot: SuggestionSnapshot,
|
||||||
pub tab_size: NonZeroU32,
|
pub tab_size: NonZeroU32,
|
||||||
|
pub max_expansion_column: u32,
|
||||||
pub version: usize,
|
pub version: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,15 +173,12 @@ impl TabSnapshot {
|
||||||
pub fn line_len(&self, row: u32) -> u32 {
|
pub fn line_len(&self, row: u32) -> u32 {
|
||||||
let max_point = self.max_point();
|
let max_point = self.max_point();
|
||||||
if row < max_point.row() {
|
if row < max_point.row() {
|
||||||
self.chunks(
|
self.to_tab_point(SuggestionPoint::new(
|
||||||
TabPoint::new(row, 0)..TabPoint::new(row + 1, 0),
|
row,
|
||||||
false,
|
self.suggestion_snapshot.line_len(row),
|
||||||
None,
|
))
|
||||||
None,
|
.0
|
||||||
)
|
.column
|
||||||
.map(|chunk| chunk.text.len() as u32)
|
|
||||||
.sum::<u32>()
|
|
||||||
- 1
|
|
||||||
} else {
|
} else {
|
||||||
max_point.column()
|
max_point.column()
|
||||||
}
|
}
|
||||||
|
@ -200,12 +246,13 @@ impl TabSnapshot {
|
||||||
) -> TabChunks<'a> {
|
) -> TabChunks<'a> {
|
||||||
let (input_start, expanded_char_column, to_next_stop) =
|
let (input_start, expanded_char_column, to_next_stop) =
|
||||||
self.to_suggestion_point(range.start, Bias::Left);
|
self.to_suggestion_point(range.start, Bias::Left);
|
||||||
|
let input_column = input_start.column();
|
||||||
let input_start = self.suggestion_snapshot.to_offset(input_start);
|
let input_start = self.suggestion_snapshot.to_offset(input_start);
|
||||||
let input_end = self
|
let input_end = self
|
||||||
.suggestion_snapshot
|
.suggestion_snapshot
|
||||||
.to_offset(self.to_suggestion_point(range.end, Bias::Right).0);
|
.to_offset(self.to_suggestion_point(range.end, Bias::Right).0);
|
||||||
let to_next_stop = if range.start.0 + Point::new(0, to_next_stop as u32) > range.end.0 {
|
let to_next_stop = if range.start.0 + Point::new(0, to_next_stop) > range.end.0 {
|
||||||
(range.end.column() - range.start.column()) as usize
|
range.end.column() - range.start.column()
|
||||||
} else {
|
} else {
|
||||||
to_next_stop
|
to_next_stop
|
||||||
};
|
};
|
||||||
|
@ -217,15 +264,17 @@ impl TabSnapshot {
|
||||||
text_highlights,
|
text_highlights,
|
||||||
suggestion_highlight,
|
suggestion_highlight,
|
||||||
),
|
),
|
||||||
|
input_column,
|
||||||
column: expanded_char_column,
|
column: expanded_char_column,
|
||||||
|
max_expansion_column: self.max_expansion_column,
|
||||||
output_position: range.start.0,
|
output_position: range.start.0,
|
||||||
max_output_position: range.end.0,
|
max_output_position: range.end.0,
|
||||||
tab_size: self.tab_size,
|
tab_size: self.tab_size,
|
||||||
chunk: Chunk {
|
chunk: Chunk {
|
||||||
text: &SPACES[0..to_next_stop],
|
text: &SPACES[0..(to_next_stop as usize)],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
skip_leading_tab: to_next_stop > 0,
|
inside_leading_tab: to_next_stop > 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,21 +304,17 @@ impl TabSnapshot {
|
||||||
let chars = self
|
let chars = self
|
||||||
.suggestion_snapshot
|
.suggestion_snapshot
|
||||||
.chars_at(SuggestionPoint::new(input.row(), 0));
|
.chars_at(SuggestionPoint::new(input.row(), 0));
|
||||||
let expanded = Self::expand_tabs(chars, input.column() as usize, self.tab_size);
|
let expanded = self.expand_tabs(chars, input.column());
|
||||||
TabPoint::new(input.row(), expanded as u32)
|
TabPoint::new(input.row(), expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_suggestion_point(
|
pub fn to_suggestion_point(&self, output: TabPoint, bias: Bias) -> (SuggestionPoint, u32, u32) {
|
||||||
&self,
|
|
||||||
output: TabPoint,
|
|
||||||
bias: Bias,
|
|
||||||
) -> (SuggestionPoint, usize, usize) {
|
|
||||||
let chars = self
|
let chars = self
|
||||||
.suggestion_snapshot
|
.suggestion_snapshot
|
||||||
.chars_at(SuggestionPoint::new(output.row(), 0));
|
.chars_at(SuggestionPoint::new(output.row(), 0));
|
||||||
let expanded = output.column() as usize;
|
let expanded = output.column();
|
||||||
let (collapsed, expanded_char_column, to_next_stop) =
|
let (collapsed, expanded_char_column, to_next_stop) =
|
||||||
Self::collapse_tabs(chars, expanded, bias, self.tab_size);
|
self.collapse_tabs(chars, expanded, bias);
|
||||||
(
|
(
|
||||||
SuggestionPoint::new(output.row(), collapsed as u32),
|
SuggestionPoint::new(output.row(), collapsed as u32),
|
||||||
expanded_char_column,
|
expanded_char_column,
|
||||||
|
@ -292,38 +337,38 @@ impl TabSnapshot {
|
||||||
fold_point.to_buffer_point(&self.suggestion_snapshot.fold_snapshot)
|
fold_point.to_buffer_point(&self.suggestion_snapshot.fold_snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_tabs(
|
fn expand_tabs(&self, chars: impl Iterator<Item = char>, column: u32) -> u32 {
|
||||||
chars: impl Iterator<Item = char>,
|
let tab_size = self.tab_size.get();
|
||||||
column: usize,
|
|
||||||
tab_size: NonZeroU32,
|
|
||||||
) -> usize {
|
|
||||||
let mut expanded_chars = 0;
|
let mut expanded_chars = 0;
|
||||||
let mut expanded_bytes = 0;
|
let mut expanded_bytes = 0;
|
||||||
let mut collapsed_bytes = 0;
|
let mut collapsed_bytes = 0;
|
||||||
|
let end_column = column.min(self.max_expansion_column);
|
||||||
for c in chars {
|
for c in chars {
|
||||||
if collapsed_bytes == column {
|
if collapsed_bytes >= end_column {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if c == '\t' {
|
if c == '\t' {
|
||||||
let tab_size = tab_size.get() as usize;
|
|
||||||
let tab_len = tab_size - expanded_chars % tab_size;
|
let tab_len = tab_size - expanded_chars % tab_size;
|
||||||
expanded_bytes += tab_len;
|
expanded_bytes += tab_len;
|
||||||
expanded_chars += tab_len;
|
expanded_chars += tab_len;
|
||||||
} else {
|
} else {
|
||||||
expanded_bytes += c.len_utf8();
|
expanded_bytes += c.len_utf8() as u32;
|
||||||
expanded_chars += 1;
|
expanded_chars += 1;
|
||||||
}
|
}
|
||||||
collapsed_bytes += c.len_utf8();
|
collapsed_bytes += c.len_utf8() as u32;
|
||||||
}
|
}
|
||||||
expanded_bytes
|
expanded_bytes + column.saturating_sub(collapsed_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collapse_tabs(
|
fn collapse_tabs(
|
||||||
|
&self,
|
||||||
chars: impl Iterator<Item = char>,
|
chars: impl Iterator<Item = char>,
|
||||||
column: usize,
|
column: u32,
|
||||||
bias: Bias,
|
bias: Bias,
|
||||||
tab_size: NonZeroU32,
|
) -> (u32, u32, u32) {
|
||||||
) -> (usize, usize, usize) {
|
let tab_size = self.tab_size.get();
|
||||||
|
|
||||||
let mut expanded_bytes = 0;
|
let mut expanded_bytes = 0;
|
||||||
let mut expanded_chars = 0;
|
let mut expanded_chars = 0;
|
||||||
let mut collapsed_bytes = 0;
|
let mut collapsed_bytes = 0;
|
||||||
|
@ -331,9 +376,11 @@ impl TabSnapshot {
|
||||||
if expanded_bytes >= column {
|
if expanded_bytes >= column {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if collapsed_bytes >= self.max_expansion_column {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if c == '\t' {
|
if c == '\t' {
|
||||||
let tab_size = tab_size.get() as usize;
|
|
||||||
let tab_len = tab_size - (expanded_chars % tab_size);
|
let tab_len = tab_size - (expanded_chars % tab_size);
|
||||||
expanded_chars += tab_len;
|
expanded_chars += tab_len;
|
||||||
expanded_bytes += tab_len;
|
expanded_bytes += tab_len;
|
||||||
|
@ -346,7 +393,7 @@ impl TabSnapshot {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
expanded_chars += 1;
|
expanded_chars += 1;
|
||||||
expanded_bytes += c.len_utf8();
|
expanded_bytes += c.len_utf8() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
if expanded_bytes > column && matches!(bias, Bias::Left) {
|
if expanded_bytes > column && matches!(bias, Bias::Left) {
|
||||||
|
@ -354,9 +401,13 @@ impl TabSnapshot {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
collapsed_bytes += c.len_utf8();
|
collapsed_bytes += c.len_utf8() as u32;
|
||||||
}
|
}
|
||||||
(collapsed_bytes, expanded_chars, 0)
|
(
|
||||||
|
collapsed_bytes + column.saturating_sub(expanded_bytes),
|
||||||
|
expanded_chars,
|
||||||
|
0,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,11 +495,13 @@ const SPACES: &str = " ";
|
||||||
pub struct TabChunks<'a> {
|
pub struct TabChunks<'a> {
|
||||||
suggestion_chunks: SuggestionChunks<'a>,
|
suggestion_chunks: SuggestionChunks<'a>,
|
||||||
chunk: Chunk<'a>,
|
chunk: Chunk<'a>,
|
||||||
column: usize,
|
column: u32,
|
||||||
|
max_expansion_column: u32,
|
||||||
output_position: Point,
|
output_position: Point,
|
||||||
|
input_column: u32,
|
||||||
max_output_position: Point,
|
max_output_position: Point,
|
||||||
tab_size: NonZeroU32,
|
tab_size: NonZeroU32,
|
||||||
skip_leading_tab: bool,
|
inside_leading_tab: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for TabChunks<'a> {
|
impl<'a> Iterator for TabChunks<'a> {
|
||||||
|
@ -458,9 +511,10 @@ impl<'a> Iterator for TabChunks<'a> {
|
||||||
if self.chunk.text.is_empty() {
|
if self.chunk.text.is_empty() {
|
||||||
if let Some(chunk) = self.suggestion_chunks.next() {
|
if let Some(chunk) = self.suggestion_chunks.next() {
|
||||||
self.chunk = chunk;
|
self.chunk = chunk;
|
||||||
if self.skip_leading_tab {
|
if self.inside_leading_tab {
|
||||||
self.chunk.text = &self.chunk.text[1..];
|
self.chunk.text = &self.chunk.text[1..];
|
||||||
self.skip_leading_tab = false;
|
self.inside_leading_tab = false;
|
||||||
|
self.input_column += 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
|
@ -479,27 +533,36 @@ impl<'a> Iterator for TabChunks<'a> {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.chunk.text = &self.chunk.text[1..];
|
self.chunk.text = &self.chunk.text[1..];
|
||||||
let tab_size = self.tab_size.get() as u32;
|
let tab_size = if self.input_column < self.max_expansion_column {
|
||||||
let mut len = tab_size - self.column as u32 % tab_size;
|
self.tab_size.get() as u32
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
let mut len = tab_size - self.column % tab_size;
|
||||||
let next_output_position = cmp::min(
|
let next_output_position = cmp::min(
|
||||||
self.output_position + Point::new(0, len),
|
self.output_position + Point::new(0, len),
|
||||||
self.max_output_position,
|
self.max_output_position,
|
||||||
);
|
);
|
||||||
len = next_output_position.column - self.output_position.column;
|
len = next_output_position.column - self.output_position.column;
|
||||||
self.column += len as usize;
|
self.column += len;
|
||||||
|
self.input_column += 1;
|
||||||
self.output_position = next_output_position;
|
self.output_position = next_output_position;
|
||||||
return Some(Chunk {
|
return Some(Chunk {
|
||||||
text: &SPACES[0..len as usize],
|
text: &SPACES[..len as usize],
|
||||||
..self.chunk
|
..self.chunk
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'\n' => {
|
'\n' => {
|
||||||
self.column = 0;
|
self.column = 0;
|
||||||
|
self.input_column = 0;
|
||||||
self.output_position += Point::new(1, 0);
|
self.output_position += Point::new(1, 0);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.column += 1;
|
self.column += 1;
|
||||||
|
if !self.inside_leading_tab {
|
||||||
|
self.input_column += c.len_utf8() as u32;
|
||||||
|
}
|
||||||
self.output_position.column += c.len_utf8() as u32;
|
self.output_position.column += c.len_utf8() as u32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -518,20 +581,83 @@ mod tests {
|
||||||
};
|
};
|
||||||
use rand::{prelude::StdRng, Rng};
|
use rand::{prelude::StdRng, Rng};
|
||||||
|
|
||||||
#[test]
|
#[gpui::test]
|
||||||
fn test_expand_tabs() {
|
fn test_expand_tabs(cx: &mut gpui::MutableAppContext) {
|
||||||
assert_eq!(
|
let buffer = MultiBuffer::build_simple("", cx);
|
||||||
TabSnapshot::expand_tabs("\t".chars(), 0, 4.try_into().unwrap()),
|
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||||
0
|
let (_, fold_snapshot) = FoldMap::new(buffer_snapshot.clone());
|
||||||
);
|
let (_, suggestion_snapshot) = SuggestionMap::new(fold_snapshot);
|
||||||
assert_eq!(
|
let (_, tab_snapshot) = TabMap::new(suggestion_snapshot, 4.try_into().unwrap());
|
||||||
TabSnapshot::expand_tabs("\t".chars(), 1, 4.try_into().unwrap()),
|
|
||||||
4
|
assert_eq!(tab_snapshot.expand_tabs("\t".chars(), 0), 0);
|
||||||
);
|
assert_eq!(tab_snapshot.expand_tabs("\t".chars(), 1), 4);
|
||||||
assert_eq!(
|
assert_eq!(tab_snapshot.expand_tabs("\ta".chars(), 2), 5);
|
||||||
TabSnapshot::expand_tabs("\ta".chars(), 2, 4.try_into().unwrap()),
|
}
|
||||||
5
|
|
||||||
);
|
#[gpui::test]
|
||||||
|
fn test_long_lines(cx: &mut gpui::MutableAppContext) {
|
||||||
|
let max_expansion_column = 12;
|
||||||
|
let input = "A\tBC\tDEF\tG\tHI\tJ\tK\tL\tM";
|
||||||
|
let output = "A BC DEF G HI J K L M";
|
||||||
|
|
||||||
|
let buffer = MultiBuffer::build_simple(input, cx);
|
||||||
|
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
let (_, fold_snapshot) = FoldMap::new(buffer_snapshot.clone());
|
||||||
|
let (_, suggestion_snapshot) = SuggestionMap::new(fold_snapshot);
|
||||||
|
let (_, mut tab_snapshot) = TabMap::new(suggestion_snapshot, 4.try_into().unwrap());
|
||||||
|
|
||||||
|
tab_snapshot.max_expansion_column = max_expansion_column;
|
||||||
|
assert_eq!(tab_snapshot.text(), output);
|
||||||
|
|
||||||
|
for (ix, c) in input.char_indices() {
|
||||||
|
assert_eq!(
|
||||||
|
tab_snapshot
|
||||||
|
.chunks(
|
||||||
|
TabPoint::new(0, ix as u32)..tab_snapshot.max_point(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.map(|c| c.text)
|
||||||
|
.collect::<String>(),
|
||||||
|
&output[ix..],
|
||||||
|
"text from index {ix}"
|
||||||
|
);
|
||||||
|
|
||||||
|
if c != '\t' {
|
||||||
|
let input_point = Point::new(0, ix as u32);
|
||||||
|
let output_point = Point::new(0, output.find(c).unwrap() as u32);
|
||||||
|
assert_eq!(
|
||||||
|
tab_snapshot.to_tab_point(SuggestionPoint(input_point)),
|
||||||
|
TabPoint(output_point),
|
||||||
|
"to_tab_point({input_point:?})"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tab_snapshot
|
||||||
|
.to_suggestion_point(TabPoint(output_point), Bias::Left)
|
||||||
|
.0,
|
||||||
|
SuggestionPoint(input_point),
|
||||||
|
"to_suggestion_point({output_point:?})"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_long_lines_with_character_spanning_max_expansion_column(
|
||||||
|
cx: &mut gpui::MutableAppContext,
|
||||||
|
) {
|
||||||
|
let max_expansion_column = 8;
|
||||||
|
let input = "abcdefg⋯hij";
|
||||||
|
|
||||||
|
let buffer = MultiBuffer::build_simple(input, cx);
|
||||||
|
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
let (_, fold_snapshot) = FoldMap::new(buffer_snapshot.clone());
|
||||||
|
let (_, suggestion_snapshot) = SuggestionMap::new(fold_snapshot);
|
||||||
|
let (_, mut tab_snapshot) = TabMap::new(suggestion_snapshot, 4.try_into().unwrap());
|
||||||
|
|
||||||
|
tab_snapshot.max_expansion_column = max_expansion_column;
|
||||||
|
assert_eq!(tab_snapshot.text(), input);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
|
@ -557,7 +683,9 @@ mod tests {
|
||||||
let (suggestion_snapshot, _) = suggestion_map.randomly_mutate(&mut rng);
|
let (suggestion_snapshot, _) = suggestion_map.randomly_mutate(&mut rng);
|
||||||
log::info!("SuggestionMap text: {:?}", suggestion_snapshot.text());
|
log::info!("SuggestionMap text: {:?}", suggestion_snapshot.text());
|
||||||
|
|
||||||
let (_, tabs_snapshot) = TabMap::new(suggestion_snapshot.clone(), tab_size);
|
let (tab_map, _) = TabMap::new(suggestion_snapshot.clone(), tab_size);
|
||||||
|
let tabs_snapshot = tab_map.set_max_expansion_column(32);
|
||||||
|
|
||||||
let text = text::Rope::from(tabs_snapshot.text().as_str());
|
let text = text::Rope::from(tabs_snapshot.text().as_str());
|
||||||
log::info!(
|
log::info!(
|
||||||
"TabMap text (tab size: {}): {:?}",
|
"TabMap text (tab size: {}): {:?}",
|
||||||
|
@ -582,11 +710,11 @@ mod tests {
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
let expected_summary = TextSummary::from(expected_text.as_str());
|
let expected_summary = TextSummary::from(expected_text.as_str());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_text,
|
|
||||||
tabs_snapshot
|
tabs_snapshot
|
||||||
.chunks(start..end, false, None, None)
|
.chunks(start..end, false, None, None)
|
||||||
.map(|c| c.text)
|
.map(|c| c.text)
|
||||||
.collect::<String>(),
|
.collect::<String>(),
|
||||||
|
expected_text,
|
||||||
"chunks({:?}..{:?})",
|
"chunks({:?}..{:?})",
|
||||||
start,
|
start,
|
||||||
end
|
end
|
||||||
|
@ -601,7 +729,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
for row in 0..=text.max_point().row {
|
for row in 0..=text.max_point().row {
|
||||||
assert_eq!(tabs_snapshot.line_len(row), text.line_len(row));
|
assert_eq!(
|
||||||
|
tabs_snapshot.line_len(row),
|
||||||
|
text.line_len(row),
|
||||||
|
"line_len({row})"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1093,7 +1093,8 @@ mod tests {
|
||||||
log::info!("FoldMap text: {:?}", fold_snapshot.text());
|
log::info!("FoldMap text: {:?}", fold_snapshot.text());
|
||||||
let (suggestion_map, suggestion_snapshot) = SuggestionMap::new(fold_snapshot.clone());
|
let (suggestion_map, suggestion_snapshot) = SuggestionMap::new(fold_snapshot.clone());
|
||||||
log::info!("SuggestionMap text: {:?}", suggestion_snapshot.text());
|
log::info!("SuggestionMap text: {:?}", suggestion_snapshot.text());
|
||||||
let (tab_map, tabs_snapshot) = TabMap::new(suggestion_snapshot.clone(), tab_size);
|
let (tab_map, _) = TabMap::new(suggestion_snapshot.clone(), tab_size);
|
||||||
|
let tabs_snapshot = tab_map.set_max_expansion_column(32);
|
||||||
log::info!("TabMap text: {:?}", tabs_snapshot.text());
|
log::info!("TabMap text: {:?}", tabs_snapshot.text());
|
||||||
|
|
||||||
let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system);
|
let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system);
|
||||||
|
|
|
@ -630,7 +630,7 @@ fn test_cancel(cx: &mut gpui::MutableAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_fold(cx: &mut gpui::MutableAppContext) {
|
fn test_fold_action(cx: &mut gpui::MutableAppContext) {
|
||||||
cx.set_global(Settings::test(cx));
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(
|
let buffer = MultiBuffer::build_simple(
|
||||||
&"
|
&"
|
||||||
|
|
|
@ -747,11 +747,15 @@ impl Item for Editor {
|
||||||
.map(|path| path.to_string_lossy().to_string())
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
.unwrap_or_else(|| "untitled".to_string());
|
.unwrap_or_else(|| "untitled".to_string());
|
||||||
|
|
||||||
let mut breadcrumbs = vec![Label::new(filename, theme.breadcrumbs.text.clone()).boxed()];
|
let filename_label = Label::new(filename, theme.workspace.breadcrumbs.default.text.clone());
|
||||||
|
let mut breadcrumbs = vec![filename_label.boxed()];
|
||||||
breadcrumbs.extend(symbols.into_iter().map(|symbol| {
|
breadcrumbs.extend(symbols.into_iter().map(|symbol| {
|
||||||
Text::new(symbol.text, theme.breadcrumbs.text.clone())
|
Text::new(
|
||||||
.with_highlights(symbol.highlight_ranges)
|
symbol.text,
|
||||||
.boxed()
|
theme.workspace.breadcrumbs.default.text.clone(),
|
||||||
|
)
|
||||||
|
.with_highlights(symbol.highlight_ranges)
|
||||||
|
.boxed()
|
||||||
}));
|
}));
|
||||||
Some(breadcrumbs)
|
Some(breadcrumbs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,12 +78,6 @@ pub trait ToLspPosition {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct LanguageServerName(pub Arc<str>);
|
pub struct LanguageServerName(pub Arc<str>);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
|
|
||||||
pub enum ServerExecutionKind {
|
|
||||||
Launch,
|
|
||||||
Node,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct LanguageServerBinary {
|
pub struct LanguageServerBinary {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "pando"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/pando.rs"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
test-support = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow = "1.0.38"
|
|
||||||
client = { path = "../client" }
|
|
||||||
gpui = { path = "../gpui" }
|
|
||||||
settings = { path = "../settings" }
|
|
||||||
theme = { path = "../theme" }
|
|
||||||
workspace = { path = "../workspace" }
|
|
||||||
sqlez = { path = "../sqlez" }
|
|
||||||
sqlez_macros = { path = "../sqlez_macros" }
|
|
|
@ -1,15 +0,0 @@
|
||||||
//! ## Goals
|
|
||||||
//! - Opinionated Subset of Obsidian. Only the things that cant be done other ways in zed
|
|
||||||
//! - Checked in .zp file is an sqlite db containing graph metadata
|
|
||||||
//! - All nodes are file urls
|
|
||||||
//! - Markdown links auto add soft linked nodes to the db
|
|
||||||
//! - Links create positioning data regardless of if theres a file
|
|
||||||
//! - Lock links to make structure that doesn't rotate or spread
|
|
||||||
//! - Drag from file finder to pando item to add it in
|
|
||||||
//! - For linked files, zoom out to see closest linking pando file
|
|
||||||
|
|
||||||
//! ## Plan
|
|
||||||
//! - [ ] Make item backed by .zp sqlite file with camera position by user account
|
|
||||||
//! - [ ] Render grid of dots and allow scrolling around the grid
|
|
||||||
//! - [ ] Add scale property to layer canvas and manipulate it with pinch zooming
|
|
||||||
//! - [ ] Allow dropping files onto .zp pane. Their relative path is recorded into the file along with
|
|
|
@ -612,7 +612,7 @@ impl Item for TerminalView {
|
||||||
fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<ElementBox>> {
|
fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<ElementBox>> {
|
||||||
Some(vec![Text::new(
|
Some(vec![Text::new(
|
||||||
self.terminal().read(cx).breadcrumb_text.clone(),
|
self.terminal().read(cx).breadcrumb_text.clone(),
|
||||||
theme.breadcrumbs.text.clone(),
|
theme.workspace.breadcrumbs.default.text.clone(),
|
||||||
)
|
)
|
||||||
.boxed()])
|
.boxed()])
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ pub struct Theme {
|
||||||
pub editor: Editor,
|
pub editor: Editor,
|
||||||
pub search: Search,
|
pub search: Search,
|
||||||
pub project_diagnostics: ProjectDiagnostics,
|
pub project_diagnostics: ProjectDiagnostics,
|
||||||
pub breadcrumbs: ContainedText,
|
|
||||||
pub shared_screen: ContainerStyle,
|
pub shared_screen: ContainerStyle,
|
||||||
pub contact_notification: ContactNotification,
|
pub contact_notification: ContactNotification,
|
||||||
pub update_notification: UpdateNotification,
|
pub update_notification: UpdateNotification,
|
||||||
|
@ -63,6 +62,8 @@ pub struct Workspace {
|
||||||
pub sidebar: Sidebar,
|
pub sidebar: Sidebar,
|
||||||
pub status_bar: StatusBar,
|
pub status_bar: StatusBar,
|
||||||
pub toolbar: Toolbar,
|
pub toolbar: Toolbar,
|
||||||
|
pub breadcrumb_height: f32,
|
||||||
|
pub breadcrumbs: Interactive<ContainedText>,
|
||||||
pub disconnected_overlay: ContainedText,
|
pub disconnected_overlay: ContainedText,
|
||||||
pub modal: ContainerStyle,
|
pub modal: ContainerStyle,
|
||||||
pub notification: ContainerStyle,
|
pub notification: ContainerStyle,
|
||||||
|
|
|
@ -1603,6 +1603,10 @@ impl View for Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||||
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
|
toolbar.pane_focus_update(true, cx);
|
||||||
|
});
|
||||||
|
|
||||||
if let Some(active_item) = self.active_item() {
|
if let Some(active_item) = self.active_item() {
|
||||||
if cx.is_self_focused() {
|
if cx.is_self_focused() {
|
||||||
// Pane was focused directly. We need to either focus a view inside the active item,
|
// Pane was focused directly. We need to either focus a view inside the active item,
|
||||||
|
@ -1626,6 +1630,12 @@ impl View for Pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||||
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
|
toolbar.pane_focus_update(false, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
||||||
let mut keymap = Self::default_keymap_context();
|
let mut keymap = Self::default_keymap_context();
|
||||||
if self.docked.is_some() {
|
if self.docked.is_some() {
|
||||||
|
|
|
@ -20,6 +20,8 @@ pub trait ToolbarItemView: View {
|
||||||
) -> ToolbarItemLocation {
|
) -> ToolbarItemLocation {
|
||||||
current_location
|
current_location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut MutableAppContext) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ToolbarItemViewHandle {
|
trait ToolbarItemViewHandle {
|
||||||
|
@ -30,6 +32,7 @@ trait ToolbarItemViewHandle {
|
||||||
active_pane_item: Option<&dyn ItemHandle>,
|
active_pane_item: Option<&dyn ItemHandle>,
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> ToolbarItemLocation;
|
) -> ToolbarItemLocation;
|
||||||
|
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut MutableAppContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
@ -260,6 +263,12 @@ impl Toolbar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut MutableAppContext) {
|
||||||
|
for (toolbar_item, _) in self.items.iter_mut() {
|
||||||
|
toolbar_item.pane_focus_update(pane_focused, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<ViewHandle<T>> {
|
pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<ViewHandle<T>> {
|
||||||
self.items
|
self.items
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -289,6 +298,10 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
|
||||||
this.set_active_pane_item(active_pane_item, cx)
|
this.set_active_pane_item(active_pane_item, cx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut MutableAppContext) {
|
||||||
|
self.update(cx, |this, cx| this.pane_focus_update(pane_focused, cx));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle {
|
impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle {
|
||||||
|
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
||||||
description = "The fast, collaborative code editor."
|
description = "The fast, collaborative code editor."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.80.0"
|
version = "0.81.0"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
|
@ -46,12 +46,6 @@ export default function app(colorScheme: ColorScheme): Object {
|
||||||
contactList: contactList(colorScheme),
|
contactList: contactList(colorScheme),
|
||||||
search: search(colorScheme),
|
search: search(colorScheme),
|
||||||
sharedScreen: sharedScreen(colorScheme),
|
sharedScreen: sharedScreen(colorScheme),
|
||||||
breadcrumbs: {
|
|
||||||
...text(colorScheme.highest, "sans", "variant"),
|
|
||||||
padding: {
|
|
||||||
left: 6,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
updateNotification: updateNotification(colorScheme),
|
updateNotification: updateNotification(colorScheme),
|
||||||
simpleMessageNotification: simpleMessageNotification(colorScheme),
|
simpleMessageNotification: simpleMessageNotification(colorScheme),
|
||||||
tooltip: tooltip(colorScheme),
|
tooltip: tooltip(colorScheme),
|
||||||
|
|
|
@ -263,9 +263,22 @@ export default function workspace(colorScheme: ColorScheme) {
|
||||||
},
|
},
|
||||||
padding: { left: 8, right: 8, top: 4, bottom: 4 },
|
padding: { left: 8, right: 8, top: 4, bottom: 4 },
|
||||||
},
|
},
|
||||||
|
breadcrumbHeight: 24,
|
||||||
breadcrumbs: {
|
breadcrumbs: {
|
||||||
...text(layer, "mono", "variant"),
|
...text(colorScheme.highest, "sans", "variant"),
|
||||||
padding: { left: 6 },
|
cornerRadius: 6,
|
||||||
|
padding: {
|
||||||
|
left: 6,
|
||||||
|
right: 6,
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
color: foreground(colorScheme.highest, "on", "hovered"),
|
||||||
|
background: background(
|
||||||
|
colorScheme.highest,
|
||||||
|
"on",
|
||||||
|
"hovered"
|
||||||
|
),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
disconnectedOverlay: {
|
disconnectedOverlay: {
|
||||||
...text(layer, "sans"),
|
...text(layer, "sans"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue