Avoid losing focus when block decorations go offscreen (#14815)
Release Notes: - Fixed a bug that caused focus to be lost when renames and inline assists were scrolled offscreen. --------- Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
parent
f5d50f2b1e
commit
d61eaea4b9
18 changed files with 941 additions and 584 deletions
|
@ -23,7 +23,8 @@ use collections::{BTreeSet, HashMap, HashSet};
|
||||||
use editor::{
|
use editor::{
|
||||||
actions::{FoldAt, MoveToEndOfLine, Newline, ShowCompletions, UnfoldAt},
|
actions::{FoldAt, MoveToEndOfLine, Newline, ShowCompletions, UnfoldAt},
|
||||||
display_map::{
|
display_map::{
|
||||||
BlockDisposition, BlockId, BlockProperties, BlockStyle, Crease, RenderBlock, ToDisplayPoint,
|
BlockDisposition, BlockProperties, BlockStyle, Crease, CustomBlockId, RenderBlock,
|
||||||
|
ToDisplayPoint,
|
||||||
},
|
},
|
||||||
scroll::{Autoscroll, AutoscrollStrategy, ScrollAnchor},
|
scroll::{Autoscroll, AutoscrollStrategy, ScrollAnchor},
|
||||||
Anchor, Editor, EditorEvent, ExcerptRange, MultiBuffer, RowExt, ToOffset as _, ToPoint,
|
Anchor, Editor, EditorEvent, ExcerptRange, MultiBuffer, RowExt, ToOffset as _, ToPoint,
|
||||||
|
@ -984,11 +985,11 @@ pub struct ContextEditor {
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
editor: View<Editor>,
|
editor: View<Editor>,
|
||||||
blocks: HashSet<BlockId>,
|
blocks: HashSet<CustomBlockId>,
|
||||||
scroll_position: Option<ScrollPosition>,
|
scroll_position: Option<ScrollPosition>,
|
||||||
remote_id: Option<workspace::ViewId>,
|
remote_id: Option<workspace::ViewId>,
|
||||||
pending_slash_command_creases: HashMap<Range<language::Anchor>, CreaseId>,
|
pending_slash_command_creases: HashMap<Range<language::Anchor>, CreaseId>,
|
||||||
pending_slash_command_blocks: HashMap<Range<language::Anchor>, BlockId>,
|
pending_slash_command_blocks: HashMap<Range<language::Anchor>, CustomBlockId>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
active_edit_step: Option<ActiveEditStep>,
|
active_edit_step: Option<ActiveEditStep>,
|
||||||
assistant_panel: WeakView<AssistantPanel>,
|
assistant_panel: WeakView<AssistantPanel>,
|
||||||
|
|
|
@ -9,7 +9,7 @@ use collections::{hash_map, HashMap, HashSet, VecDeque};
|
||||||
use editor::{
|
use editor::{
|
||||||
actions::{MoveDown, MoveUp, SelectAll},
|
actions::{MoveDown, MoveUp, SelectAll},
|
||||||
display_map::{
|
display_map::{
|
||||||
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock,
|
BlockContext, BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
|
||||||
ToDisplayPoint,
|
ToDisplayPoint,
|
||||||
},
|
},
|
||||||
Anchor, AnchorRangeExt, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle,
|
Anchor, AnchorRangeExt, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle,
|
||||||
|
@ -310,7 +310,7 @@ impl InlineAssistant {
|
||||||
range: &Range<Anchor>,
|
range: &Range<Anchor>,
|
||||||
prompt_editor: &View<PromptEditor>,
|
prompt_editor: &View<PromptEditor>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> [BlockId; 2] {
|
) -> [CustomBlockId; 2] {
|
||||||
let assist_blocks = vec![
|
let assist_blocks = vec![
|
||||||
BlockProperties {
|
BlockProperties {
|
||||||
style: BlockStyle::Sticky,
|
style: BlockStyle::Sticky,
|
||||||
|
@ -1900,8 +1900,8 @@ impl InlineAssist {
|
||||||
include_context: bool,
|
include_context: bool,
|
||||||
editor: &View<Editor>,
|
editor: &View<Editor>,
|
||||||
prompt_editor: &View<PromptEditor>,
|
prompt_editor: &View<PromptEditor>,
|
||||||
prompt_block_id: BlockId,
|
prompt_block_id: CustomBlockId,
|
||||||
end_block_id: BlockId,
|
end_block_id: CustomBlockId,
|
||||||
codegen: Model<Codegen>,
|
codegen: Model<Codegen>,
|
||||||
workspace: Option<WeakView<Workspace>>,
|
workspace: Option<WeakView<Workspace>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -1995,10 +1995,10 @@ impl InlineAssist {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InlineAssistDecorations {
|
struct InlineAssistDecorations {
|
||||||
prompt_block_id: BlockId,
|
prompt_block_id: CustomBlockId,
|
||||||
prompt_editor: View<PromptEditor>,
|
prompt_editor: View<PromptEditor>,
|
||||||
removed_line_block_ids: HashSet<BlockId>,
|
removed_line_block_ids: HashSet<CustomBlockId>,
|
||||||
end_block_id: BlockId,
|
end_block_id: CustomBlockId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -10,7 +10,7 @@ use anyhow::Result;
|
||||||
use collections::{BTreeSet, HashSet};
|
use collections::{BTreeSet, HashSet};
|
||||||
use editor::{
|
use editor::{
|
||||||
diagnostic_block_renderer,
|
diagnostic_block_renderer,
|
||||||
display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock},
|
display_map::{BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock},
|
||||||
highlight_diagnostic_message,
|
highlight_diagnostic_message,
|
||||||
scroll::Autoscroll,
|
scroll::Autoscroll,
|
||||||
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
||||||
|
@ -85,7 +85,7 @@ struct DiagnosticGroupState {
|
||||||
primary_diagnostic: DiagnosticEntry<language::Anchor>,
|
primary_diagnostic: DiagnosticEntry<language::Anchor>,
|
||||||
primary_excerpt_ix: usize,
|
primary_excerpt_ix: usize,
|
||||||
excerpts: Vec<ExcerptId>,
|
excerpts: Vec<ExcerptId>,
|
||||||
blocks: HashSet<BlockId>,
|
blocks: HashSet<CustomBlockId>,
|
||||||
block_count: usize,
|
block_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use editor::{
|
use editor::{
|
||||||
display_map::{BlockContext, DisplayRow, TransformBlock},
|
display_map::{Block, BlockContext, DisplayRow},
|
||||||
DisplayPoint, GutterDimensions,
|
DisplayPoint, GutterDimensions,
|
||||||
};
|
};
|
||||||
use gpui::{px, AvailableSpace, Stateful, TestAppContext, VisualTestContext};
|
use gpui::{px, AvailableSpace, Stateful, TestAppContext, VisualTestContext};
|
||||||
|
@ -974,9 +974,9 @@ fn editor_blocks(
|
||||||
snapshot
|
snapshot
|
||||||
.blocks_in_range(DisplayRow(0)..snapshot.max_point().row())
|
.blocks_in_range(DisplayRow(0)..snapshot.max_point().row())
|
||||||
.filter_map(|(row, block)| {
|
.filter_map(|(row, block)| {
|
||||||
let transform_block_id = block.id();
|
let block_id = block.id();
|
||||||
let name: SharedString = match block {
|
let name: SharedString = match block {
|
||||||
TransformBlock::Custom(block) => {
|
Block::Custom(block) => {
|
||||||
let mut element = block.render(&mut BlockContext {
|
let mut element = block.render(&mut BlockContext {
|
||||||
context: cx,
|
context: cx,
|
||||||
anchor_x: px(0.),
|
anchor_x: px(0.),
|
||||||
|
@ -984,7 +984,7 @@ fn editor_blocks(
|
||||||
line_height: px(0.),
|
line_height: px(0.),
|
||||||
em_width: px(0.),
|
em_width: px(0.),
|
||||||
max_width: px(0.),
|
max_width: px(0.),
|
||||||
transform_block_id,
|
block_id,
|
||||||
editor_style: &editor::EditorStyle::default(),
|
editor_style: &editor::EditorStyle::default(),
|
||||||
});
|
});
|
||||||
let element = element.downcast_mut::<Stateful<Div>>().unwrap();
|
let element = element.downcast_mut::<Stateful<Div>>().unwrap();
|
||||||
|
@ -996,7 +996,7 @@ fn editor_blocks(
|
||||||
.ok()?
|
.ok()?
|
||||||
}
|
}
|
||||||
|
|
||||||
TransformBlock::ExcerptHeader {
|
Block::ExcerptHeader {
|
||||||
starts_new_buffer, ..
|
starts_new_buffer, ..
|
||||||
} => {
|
} => {
|
||||||
if *starts_new_buffer {
|
if *starts_new_buffer {
|
||||||
|
@ -1005,7 +1005,7 @@ fn editor_blocks(
|
||||||
EXCERPT_HEADER.into()
|
EXCERPT_HEADER.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TransformBlock::ExcerptFooter { .. } => EXCERPT_FOOTER.into(),
|
Block::ExcerptFooter { .. } => EXCERPT_FOOTER.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((row, name))
|
Some((row, name))
|
||||||
|
|
|
@ -3,8 +3,8 @@ use collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
use editor::{
|
use editor::{
|
||||||
diagnostic_block_renderer,
|
diagnostic_block_renderer,
|
||||||
display_map::{
|
display_map::{
|
||||||
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock,
|
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, CustomBlockId,
|
||||||
TransformBlockId,
|
RenderBlock,
|
||||||
},
|
},
|
||||||
scroll::Autoscroll,
|
scroll::Autoscroll,
|
||||||
Bias, Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToPoint,
|
Bias, Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToPoint,
|
||||||
|
@ -71,7 +71,7 @@ struct PathState {
|
||||||
path: ProjectPath,
|
path: ProjectPath,
|
||||||
first_excerpt_id: Option<ExcerptId>,
|
first_excerpt_id: Option<ExcerptId>,
|
||||||
last_excerpt_id: Option<ExcerptId>,
|
last_excerpt_id: Option<ExcerptId>,
|
||||||
diagnostics: Vec<(DiagnosticData, BlockId)>,
|
diagnostics: Vec<(DiagnosticData, CustomBlockId)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -657,10 +657,10 @@ fn compare_diagnostic_range_edges(
|
||||||
struct PathUpdate {
|
struct PathUpdate {
|
||||||
path_excerpts_borders: (Option<ExcerptId>, Option<ExcerptId>),
|
path_excerpts_borders: (Option<ExcerptId>, Option<ExcerptId>),
|
||||||
latest_excerpt_id: ExcerptId,
|
latest_excerpt_id: ExcerptId,
|
||||||
new_diagnostics: Vec<(DiagnosticData, Option<BlockId>)>,
|
new_diagnostics: Vec<(DiagnosticData, Option<CustomBlockId>)>,
|
||||||
diagnostics_by_row_label: BTreeMap<MultiBufferRow, (editor::Anchor, Vec<usize>)>,
|
diagnostics_by_row_label: BTreeMap<MultiBufferRow, (editor::Anchor, Vec<usize>)>,
|
||||||
blocks_to_remove: HashSet<BlockId>,
|
blocks_to_remove: HashSet<CustomBlockId>,
|
||||||
unchanged_blocks: HashMap<usize, BlockId>,
|
unchanged_blocks: HashMap<usize, CustomBlockId>,
|
||||||
excerpts_with_new_diagnostics: HashSet<ExcerptId>,
|
excerpts_with_new_diagnostics: HashSet<ExcerptId>,
|
||||||
excerpts_to_remove: Vec<ExcerptId>,
|
excerpts_to_remove: Vec<ExcerptId>,
|
||||||
excerpt_expands: HashMap<(ExpandExcerptDirection, u32), Vec<ExcerptId>>,
|
excerpt_expands: HashMap<(ExpandExcerptDirection, u32), Vec<ExcerptId>>,
|
||||||
|
@ -749,7 +749,7 @@ impl PathUpdate {
|
||||||
context: u32,
|
context: u32,
|
||||||
multi_buffer_snapshot: MultiBufferSnapshot,
|
multi_buffer_snapshot: MultiBufferSnapshot,
|
||||||
buffer_snapshot: BufferSnapshot,
|
buffer_snapshot: BufferSnapshot,
|
||||||
current_diagnostics: impl Iterator<Item = &'a (DiagnosticData, BlockId)> + 'a,
|
current_diagnostics: impl Iterator<Item = &'a (DiagnosticData, CustomBlockId)> + 'a,
|
||||||
) {
|
) {
|
||||||
let mut current_diagnostics = current_diagnostics.fuse().peekable();
|
let mut current_diagnostics = current_diagnostics.fuse().peekable();
|
||||||
let mut excerpts_to_expand =
|
let mut excerpts_to_expand =
|
||||||
|
@ -1234,7 +1234,10 @@ impl PathUpdate {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_blocks(mut self, new_block_ids: Vec<BlockId>) -> Vec<(DiagnosticData, BlockId)> {
|
fn new_blocks(
|
||||||
|
mut self,
|
||||||
|
new_block_ids: Vec<CustomBlockId>,
|
||||||
|
) -> Vec<(DiagnosticData, CustomBlockId)> {
|
||||||
let mut new_block_ids = new_block_ids.into_iter().fuse();
|
let mut new_block_ids = new_block_ids.into_iter().fuse();
|
||||||
for (_, (_, grouped_diagnostics)) in self.diagnostics_by_row_label {
|
for (_, (_, grouped_diagnostics)) in self.diagnostics_by_row_label {
|
||||||
let mut created_block_id = None;
|
let mut created_block_id = None;
|
||||||
|
@ -1285,8 +1288,8 @@ fn render_same_line_diagnostics(
|
||||||
folded_block_height: u8,
|
folded_block_height: u8,
|
||||||
) -> RenderBlock {
|
) -> RenderBlock {
|
||||||
Box::new(move |cx: &mut BlockContext| {
|
Box::new(move |cx: &mut BlockContext| {
|
||||||
let block_id = match cx.transform_block_id {
|
let block_id = match cx.block_id {
|
||||||
TransformBlockId::Block(block_id) => block_id,
|
BlockId::Custom(block_id) => block_id,
|
||||||
_ => {
|
_ => {
|
||||||
debug_panic!("Expected a block id for the diagnostics block");
|
debug_panic!("Expected a block id for the diagnostics block");
|
||||||
return div().into_any_element();
|
return div().into_any_element();
|
||||||
|
@ -1320,7 +1323,7 @@ fn render_same_line_diagnostics(
|
||||||
.child(v_flex().size_full().when_some_else(
|
.child(v_flex().size_full().when_some_else(
|
||||||
toggle_expand_label,
|
toggle_expand_label,
|
||||||
|parent, label| {
|
|parent, label| {
|
||||||
parent.child(Button::new(cx.transform_block_id, label).on_click({
|
parent.child(Button::new(cx.block_id, label).on_click({
|
||||||
let diagnostics = Arc::clone(&diagnostics);
|
let diagnostics = Arc::clone(&diagnostics);
|
||||||
move |_, cx| {
|
move |_, cx| {
|
||||||
let new_expanded = !expanded;
|
let new_expanded = !expanded;
|
||||||
|
|
|
@ -28,9 +28,8 @@ use crate::{
|
||||||
hover_links::InlayHighlight, movement::TextLayoutDetails, EditorStyle, InlayId, RowExt,
|
hover_links::InlayHighlight, movement::TextLayoutDetails, EditorStyle, InlayId, RowExt,
|
||||||
};
|
};
|
||||||
pub use block_map::{
|
pub use block_map::{
|
||||||
BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,
|
Block, BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,
|
||||||
BlockMap, BlockPoint, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
|
BlockMap, BlockPoint, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
|
||||||
TransformBlockId,
|
|
||||||
};
|
};
|
||||||
use block_map::{BlockRow, BlockSnapshot};
|
use block_map::{BlockRow, BlockSnapshot};
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
|
@ -270,7 +269,7 @@ impl DisplayMap {
|
||||||
&mut self,
|
&mut self,
|
||||||
blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
|
blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Vec<BlockId> {
|
) -> Vec<CustomBlockId> {
|
||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
let tab_size = Self::tab_size(&self.buffer, cx);
|
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||||
|
@ -286,7 +285,7 @@ impl DisplayMap {
|
||||||
|
|
||||||
pub fn replace_blocks(
|
pub fn replace_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
heights_and_renderers: HashMap<BlockId, (Option<u8>, RenderBlock)>,
|
heights_and_renderers: HashMap<CustomBlockId, (Option<u8>, RenderBlock)>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
|
@ -307,8 +306,8 @@ impl DisplayMap {
|
||||||
// directly and the new behavior separately.
|
// directly and the new behavior separately.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
let mut only_renderers = HashMap::<BlockId, RenderBlock>::default();
|
let mut only_renderers = HashMap::<CustomBlockId, RenderBlock>::default();
|
||||||
let mut full_replace = HashMap::<BlockId, (u8, RenderBlock)>::default();
|
let mut full_replace = HashMap::<CustomBlockId, (u8, RenderBlock)>::default();
|
||||||
for (id, (height, render)) in heights_and_renderers {
|
for (id, (height, render)) in heights_and_renderers {
|
||||||
if let Some(height) = height {
|
if let Some(height) = height {
|
||||||
full_replace.insert(id, (height, render));
|
full_replace.insert(id, (height, render));
|
||||||
|
@ -335,7 +334,7 @@ impl DisplayMap {
|
||||||
block_map.replace(full_replace);
|
block_map.replace(full_replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<Self>) {
|
pub fn remove_blocks(&mut self, ids: HashSet<CustomBlockId>, cx: &mut ModelContext<Self>) {
|
||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
let tab_size = Self::tab_size(&self.buffer, cx);
|
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||||
|
@ -351,7 +350,7 @@ impl DisplayMap {
|
||||||
|
|
||||||
pub fn row_for_block(
|
pub fn row_for_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
block_id: BlockId,
|
block_id: CustomBlockId,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Option<DisplayRow> {
|
) -> Option<DisplayRow> {
|
||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
|
@ -886,12 +885,16 @@ impl DisplaySnapshot {
|
||||||
pub fn blocks_in_range(
|
pub fn blocks_in_range(
|
||||||
&self,
|
&self,
|
||||||
rows: Range<DisplayRow>,
|
rows: Range<DisplayRow>,
|
||||||
) -> impl Iterator<Item = (DisplayRow, &TransformBlock)> {
|
) -> impl Iterator<Item = (DisplayRow, &Block)> {
|
||||||
self.block_snapshot
|
self.block_snapshot
|
||||||
.blocks_in_range(rows.start.0..rows.end.0)
|
.blocks_in_range(rows.start.0..rows.end.0)
|
||||||
.map(|(row, block)| (DisplayRow(row), block))
|
.map(|(row, block)| (DisplayRow(row), block))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn block_for_id(&self, id: BlockId) -> Option<Block> {
|
||||||
|
self.block_snapshot.block_for_id(id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
|
pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
|
||||||
self.fold_snapshot.intersects_fold(offset)
|
self.fold_snapshot.intersects_fold(offset)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::{
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use sum_tree::{Bias, SumTree};
|
use sum_tree::{Bias, SumTree, TreeMap};
|
||||||
use text::Edit;
|
use text::Edit;
|
||||||
use ui::ElementId;
|
use ui::ElementId;
|
||||||
|
|
||||||
|
@ -30,7 +30,8 @@ const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
|
||||||
pub struct BlockMap {
|
pub struct BlockMap {
|
||||||
next_block_id: AtomicUsize,
|
next_block_id: AtomicUsize,
|
||||||
wrap_snapshot: RefCell<WrapSnapshot>,
|
wrap_snapshot: RefCell<WrapSnapshot>,
|
||||||
blocks: Vec<Arc<Block>>,
|
custom_blocks: Vec<Arc<CustomBlock>>,
|
||||||
|
custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
|
||||||
transforms: RefCell<SumTree<Transform>>,
|
transforms: RefCell<SumTree<Transform>>,
|
||||||
show_excerpt_controls: bool,
|
show_excerpt_controls: bool,
|
||||||
buffer_header_height: u8,
|
buffer_header_height: u8,
|
||||||
|
@ -39,7 +40,7 @@ pub struct BlockMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockMapReader<'a> {
|
pub struct BlockMapReader<'a> {
|
||||||
blocks: &'a Vec<Arc<Block>>,
|
blocks: &'a Vec<Arc<CustomBlock>>,
|
||||||
pub snapshot: BlockSnapshot,
|
pub snapshot: BlockSnapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,12 +50,13 @@ pub struct BlockMapWriter<'a>(&'a mut BlockMap);
|
||||||
pub struct BlockSnapshot {
|
pub struct BlockSnapshot {
|
||||||
wrap_snapshot: WrapSnapshot,
|
wrap_snapshot: WrapSnapshot,
|
||||||
transforms: SumTree<Transform>,
|
transforms: SumTree<Transform>,
|
||||||
|
custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct BlockId(usize);
|
pub struct CustomBlockId(usize);
|
||||||
|
|
||||||
impl Into<ElementId> for BlockId {
|
impl Into<ElementId> for CustomBlockId {
|
||||||
fn into(self) -> ElementId {
|
fn into(self) -> ElementId {
|
||||||
ElementId::Integer(self.0)
|
ElementId::Integer(self.0)
|
||||||
}
|
}
|
||||||
|
@ -71,8 +73,8 @@ struct WrapRow(u32);
|
||||||
|
|
||||||
pub type RenderBlock = Box<dyn Send + FnMut(&mut BlockContext) -> AnyElement>;
|
pub type RenderBlock = Box<dyn Send + FnMut(&mut BlockContext) -> AnyElement>;
|
||||||
|
|
||||||
pub struct Block {
|
pub struct CustomBlock {
|
||||||
id: BlockId,
|
id: CustomBlockId,
|
||||||
position: Anchor,
|
position: Anchor,
|
||||||
height: u8,
|
height: u8,
|
||||||
style: BlockStyle,
|
style: BlockStyle,
|
||||||
|
@ -113,41 +115,41 @@ pub struct BlockContext<'a, 'b> {
|
||||||
pub gutter_dimensions: &'b GutterDimensions,
|
pub gutter_dimensions: &'b GutterDimensions,
|
||||||
pub em_width: Pixels,
|
pub em_width: Pixels,
|
||||||
pub line_height: Pixels,
|
pub line_height: Pixels,
|
||||||
pub transform_block_id: TransformBlockId,
|
pub block_id: BlockId,
|
||||||
pub editor_style: &'b EditorStyle,
|
pub editor_style: &'b EditorStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum TransformBlockId {
|
pub enum BlockId {
|
||||||
Block(BlockId),
|
Custom(CustomBlockId),
|
||||||
ExcerptHeader(ExcerptId),
|
ExcerptHeader(ExcerptId),
|
||||||
ExcerptFooter(ExcerptId),
|
ExcerptFooter(ExcerptId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TransformBlockId> for EntityId {
|
impl From<BlockId> for EntityId {
|
||||||
fn from(value: TransformBlockId) -> Self {
|
fn from(value: BlockId) -> Self {
|
||||||
match value {
|
match value {
|
||||||
TransformBlockId::Block(BlockId(id)) => EntityId::from(id as u64),
|
BlockId::Custom(CustomBlockId(id)) => EntityId::from(id as u64),
|
||||||
TransformBlockId::ExcerptHeader(id) => id.into(),
|
BlockId::ExcerptHeader(id) => id.into(),
|
||||||
TransformBlockId::ExcerptFooter(id) => id.into(),
|
BlockId::ExcerptFooter(id) => id.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<ElementId> for TransformBlockId {
|
impl Into<ElementId> for BlockId {
|
||||||
fn into(self) -> ElementId {
|
fn into(self) -> ElementId {
|
||||||
match self {
|
match self {
|
||||||
Self::Block(BlockId(id)) => ("Block", id).into(),
|
Self::Custom(CustomBlockId(id)) => ("Block", id).into(),
|
||||||
Self::ExcerptHeader(id) => ("ExcerptHeader", EntityId::from(id)).into(),
|
Self::ExcerptHeader(id) => ("ExcerptHeader", EntityId::from(id)).into(),
|
||||||
Self::ExcerptFooter(id) => ("ExcerptFooter", EntityId::from(id)).into(),
|
Self::ExcerptFooter(id) => ("ExcerptFooter", EntityId::from(id)).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for TransformBlockId {
|
impl std::fmt::Display for BlockId {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Block(id) => write!(f, "Block({id:?})"),
|
Self::Custom(id) => write!(f, "Block({id:?})"),
|
||||||
Self::ExcerptHeader(id) => write!(f, "ExcerptHeader({id:?})"),
|
Self::ExcerptHeader(id) => write!(f, "ExcerptHeader({id:?})"),
|
||||||
Self::ExcerptFooter(id) => write!(f, "ExcerptFooter({id:?})"),
|
Self::ExcerptFooter(id) => write!(f, "ExcerptFooter({id:?})"),
|
||||||
}
|
}
|
||||||
|
@ -164,11 +166,11 @@ pub enum BlockDisposition {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct Transform {
|
struct Transform {
|
||||||
summary: TransformSummary,
|
summary: TransformSummary,
|
||||||
block: Option<TransformBlock>,
|
block: Option<Block>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum BlockType {
|
pub(crate) enum BlockType {
|
||||||
Custom(BlockId),
|
Custom(CustomBlockId),
|
||||||
Header,
|
Header,
|
||||||
Footer,
|
Footer,
|
||||||
}
|
}
|
||||||
|
@ -180,8 +182,8 @@ pub(crate) trait BlockLike {
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum TransformBlock {
|
pub enum Block {
|
||||||
Custom(Arc<Block>),
|
Custom(Arc<CustomBlock>),
|
||||||
ExcerptHeader {
|
ExcerptHeader {
|
||||||
id: ExcerptId,
|
id: ExcerptId,
|
||||||
buffer: BufferSnapshot,
|
buffer: BufferSnapshot,
|
||||||
|
@ -197,12 +199,12 @@ pub enum TransformBlock {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockLike for TransformBlock {
|
impl BlockLike for Block {
|
||||||
fn block_type(&self) -> BlockType {
|
fn block_type(&self) -> BlockType {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom(block) => BlockType::Custom(block.id),
|
Block::Custom(block) => BlockType::Custom(block.id),
|
||||||
TransformBlock::ExcerptHeader { .. } => BlockType::Header,
|
Block::ExcerptHeader { .. } => BlockType::Header,
|
||||||
TransformBlock::ExcerptFooter { .. } => BlockType::Footer,
|
Block::ExcerptFooter { .. } => BlockType::Footer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,33 +213,41 @@ impl BlockLike for TransformBlock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformBlock {
|
impl Block {
|
||||||
pub fn id(&self) -> TransformBlockId {
|
pub fn id(&self) -> BlockId {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom(block) => TransformBlockId::Block(block.id),
|
Block::Custom(block) => BlockId::Custom(block.id),
|
||||||
TransformBlock::ExcerptHeader { id, .. } => TransformBlockId::ExcerptHeader(*id),
|
Block::ExcerptHeader { id, .. } => BlockId::ExcerptHeader(*id),
|
||||||
TransformBlock::ExcerptFooter { id, .. } => TransformBlockId::ExcerptFooter(*id),
|
Block::ExcerptFooter { id, .. } => BlockId::ExcerptFooter(*id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn disposition(&self) -> BlockDisposition {
|
fn disposition(&self) -> BlockDisposition {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom(block) => block.disposition,
|
Block::Custom(block) => block.disposition,
|
||||||
TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
|
Block::ExcerptHeader { .. } => BlockDisposition::Above,
|
||||||
TransformBlock::ExcerptFooter { disposition, .. } => *disposition,
|
Block::ExcerptFooter { disposition, .. } => *disposition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> u8 {
|
pub fn height(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom(block) => block.height,
|
Block::Custom(block) => block.height,
|
||||||
TransformBlock::ExcerptHeader { height, .. } => *height,
|
Block::ExcerptHeader { height, .. } => *height,
|
||||||
TransformBlock::ExcerptFooter { height, .. } => *height,
|
Block::ExcerptFooter { height, .. } => *height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn style(&self) -> BlockStyle {
|
||||||
|
match self {
|
||||||
|
Block::Custom(block) => block.style,
|
||||||
|
Block::ExcerptHeader { .. } => BlockStyle::Sticky,
|
||||||
|
Block::ExcerptFooter { .. } => BlockStyle::Sticky,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for TransformBlock {
|
impl Debug for Block {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
|
Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
|
||||||
|
@ -252,7 +262,7 @@ impl Debug for TransformBlock {
|
||||||
.field("path", &buffer.file().map(|f| f.path()))
|
.field("path", &buffer.file().map(|f| f.path()))
|
||||||
.field("starts_new_buffer", &starts_new_buffer)
|
.field("starts_new_buffer", &starts_new_buffer)
|
||||||
.finish(),
|
.finish(),
|
||||||
TransformBlock::ExcerptFooter {
|
Block::ExcerptFooter {
|
||||||
id, disposition, ..
|
id, disposition, ..
|
||||||
} => f
|
} => f
|
||||||
.debug_struct("ExcerptFooter")
|
.debug_struct("ExcerptFooter")
|
||||||
|
@ -296,7 +306,8 @@ impl BlockMap {
|
||||||
let row_count = wrap_snapshot.max_point().row() + 1;
|
let row_count = wrap_snapshot.max_point().row() + 1;
|
||||||
let map = Self {
|
let map = Self {
|
||||||
next_block_id: AtomicUsize::new(0),
|
next_block_id: AtomicUsize::new(0),
|
||||||
blocks: Vec::new(),
|
custom_blocks: Vec::new(),
|
||||||
|
custom_blocks_by_id: TreeMap::default(),
|
||||||
transforms: RefCell::new(SumTree::from_item(Transform::isomorphic(row_count), &())),
|
transforms: RefCell::new(SumTree::from_item(Transform::isomorphic(row_count), &())),
|
||||||
wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
|
wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
|
||||||
show_excerpt_controls,
|
show_excerpt_controls,
|
||||||
|
@ -318,10 +329,11 @@ impl BlockMap {
|
||||||
self.sync(&wrap_snapshot, edits);
|
self.sync(&wrap_snapshot, edits);
|
||||||
*self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
|
*self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
|
||||||
BlockMapReader {
|
BlockMapReader {
|
||||||
blocks: &self.blocks,
|
blocks: &self.custom_blocks,
|
||||||
snapshot: BlockSnapshot {
|
snapshot: BlockSnapshot {
|
||||||
wrap_snapshot,
|
wrap_snapshot,
|
||||||
transforms: self.transforms.borrow().clone(),
|
transforms: self.transforms.borrow().clone(),
|
||||||
|
custom_blocks_by_id: self.custom_blocks_by_id.clone(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -443,25 +455,26 @@ impl BlockMap {
|
||||||
let new_buffer_start =
|
let new_buffer_start =
|
||||||
wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
|
wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
|
||||||
let start_bound = Bound::Included(new_buffer_start);
|
let start_bound = Bound::Included(new_buffer_start);
|
||||||
let start_block_ix = match self.blocks[last_block_ix..].binary_search_by(|probe| {
|
let start_block_ix =
|
||||||
probe
|
match self.custom_blocks[last_block_ix..].binary_search_by(|probe| {
|
||||||
.position
|
probe
|
||||||
.to_point(buffer)
|
.position
|
||||||
.cmp(&new_buffer_start)
|
.to_point(buffer)
|
||||||
.then(Ordering::Greater)
|
.cmp(&new_buffer_start)
|
||||||
}) {
|
.then(Ordering::Greater)
|
||||||
Ok(ix) | Err(ix) => last_block_ix + ix,
|
}) {
|
||||||
};
|
Ok(ix) | Err(ix) => last_block_ix + ix,
|
||||||
|
};
|
||||||
|
|
||||||
let end_bound;
|
let end_bound;
|
||||||
let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
|
let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
|
||||||
end_bound = Bound::Unbounded;
|
end_bound = Bound::Unbounded;
|
||||||
self.blocks.len()
|
self.custom_blocks.len()
|
||||||
} else {
|
} else {
|
||||||
let new_buffer_end =
|
let new_buffer_end =
|
||||||
wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
|
wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
|
||||||
end_bound = Bound::Excluded(new_buffer_end);
|
end_bound = Bound::Excluded(new_buffer_end);
|
||||||
match self.blocks[start_block_ix..].binary_search_by(|probe| {
|
match self.custom_blocks[start_block_ix..].binary_search_by(|probe| {
|
||||||
probe
|
probe
|
||||||
.position
|
.position
|
||||||
.to_point(buffer)
|
.to_point(buffer)
|
||||||
|
@ -474,24 +487,22 @@ impl BlockMap {
|
||||||
last_block_ix = end_block_ix;
|
last_block_ix = end_block_ix;
|
||||||
|
|
||||||
debug_assert!(blocks_in_edit.is_empty());
|
debug_assert!(blocks_in_edit.is_empty());
|
||||||
blocks_in_edit.extend(
|
blocks_in_edit.extend(self.custom_blocks[start_block_ix..end_block_ix].iter().map(
|
||||||
self.blocks[start_block_ix..end_block_ix]
|
|block| {
|
||||||
.iter()
|
let mut position = block.position.to_point(buffer);
|
||||||
.map(|block| {
|
match block.disposition {
|
||||||
let mut position = block.position.to_point(buffer);
|
BlockDisposition::Above => position.column = 0,
|
||||||
match block.disposition {
|
BlockDisposition::Below => {
|
||||||
BlockDisposition::Above => position.column = 0,
|
position.column = buffer.line_len(MultiBufferRow(position.row))
|
||||||
BlockDisposition::Below => {
|
|
||||||
position.column = buffer.line_len(MultiBufferRow(position.row))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let position = wrap_snapshot.make_wrap_point(position, Bias::Left);
|
}
|
||||||
(position.row(), TransformBlock::Custom(block.clone()))
|
let position = wrap_snapshot.make_wrap_point(position, Bias::Left);
|
||||||
}),
|
(position.row(), Block::Custom(block.clone()))
|
||||||
);
|
},
|
||||||
|
));
|
||||||
|
|
||||||
if buffer.show_headers() {
|
if buffer.show_headers() {
|
||||||
blocks_in_edit.extend(BlockMap::header_blocks(
|
blocks_in_edit.extend(BlockMap::header_and_footer_blocks(
|
||||||
self.show_excerpt_controls,
|
self.show_excerpt_controls,
|
||||||
self.excerpt_footer_height,
|
self.excerpt_footer_height,
|
||||||
self.buffer_header_height,
|
self.buffer_header_height,
|
||||||
|
@ -538,8 +549,8 @@ impl BlockMap {
|
||||||
*transforms = new_transforms;
|
*transforms = new_transforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_renderers(&mut self, mut renderers: HashMap<BlockId, RenderBlock>) {
|
pub fn replace_renderers(&mut self, mut renderers: HashMap<CustomBlockId, RenderBlock>) {
|
||||||
for block in &mut self.blocks {
|
for block in &mut self.custom_blocks {
|
||||||
if let Some(render) = renderers.remove(&block.id) {
|
if let Some(render) = renderers.remove(&block.id) {
|
||||||
*block.render.lock() = render;
|
*block.render.lock() = render;
|
||||||
}
|
}
|
||||||
|
@ -550,7 +561,7 @@ impl BlockMap {
|
||||||
self.show_excerpt_controls
|
self.show_excerpt_controls
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn header_blocks<'a, 'b: 'a, 'c: 'a + 'b, R, T>(
|
pub fn header_and_footer_blocks<'a, 'b: 'a, 'c: 'a + 'b, R, T>(
|
||||||
show_excerpt_controls: bool,
|
show_excerpt_controls: bool,
|
||||||
excerpt_footer_height: u8,
|
excerpt_footer_height: u8,
|
||||||
buffer_header_height: u8,
|
buffer_header_height: u8,
|
||||||
|
@ -558,7 +569,7 @@ impl BlockMap {
|
||||||
buffer: &'b multi_buffer::MultiBufferSnapshot,
|
buffer: &'b multi_buffer::MultiBufferSnapshot,
|
||||||
range: R,
|
range: R,
|
||||||
wrap_snapshot: &'c WrapSnapshot,
|
wrap_snapshot: &'c WrapSnapshot,
|
||||||
) -> impl Iterator<Item = (u32, TransformBlock)> + 'b
|
) -> impl Iterator<Item = (u32, Block)> + 'b
|
||||||
where
|
where
|
||||||
R: RangeBounds<T>,
|
R: RangeBounds<T>,
|
||||||
T: multi_buffer::ToOffset,
|
T: multi_buffer::ToOffset,
|
||||||
|
@ -566,24 +577,36 @@ impl BlockMap {
|
||||||
buffer
|
buffer
|
||||||
.excerpt_boundaries_in_range(range)
|
.excerpt_boundaries_in_range(range)
|
||||||
.flat_map(move |excerpt_boundary| {
|
.flat_map(move |excerpt_boundary| {
|
||||||
let wrap_row = wrap_snapshot
|
let mut wrap_row = wrap_snapshot
|
||||||
.make_wrap_point(Point::new(excerpt_boundary.row.0, 0), Bias::Left)
|
.make_wrap_point(Point::new(excerpt_boundary.row.0, 0), Bias::Left)
|
||||||
.row();
|
.row();
|
||||||
|
|
||||||
[
|
[
|
||||||
show_excerpt_controls
|
show_excerpt_controls
|
||||||
.then(|| {
|
.then(|| {
|
||||||
|
let disposition;
|
||||||
|
if excerpt_boundary.next.is_some() {
|
||||||
|
disposition = BlockDisposition::Above;
|
||||||
|
} else {
|
||||||
|
wrap_row = wrap_snapshot
|
||||||
|
.make_wrap_point(
|
||||||
|
Point::new(
|
||||||
|
excerpt_boundary.row.0,
|
||||||
|
buffer.line_len(excerpt_boundary.row),
|
||||||
|
),
|
||||||
|
Bias::Left,
|
||||||
|
)
|
||||||
|
.row();
|
||||||
|
disposition = BlockDisposition::Below;
|
||||||
|
}
|
||||||
|
|
||||||
excerpt_boundary.prev.as_ref().map(|prev| {
|
excerpt_boundary.prev.as_ref().map(|prev| {
|
||||||
(
|
(
|
||||||
wrap_row,
|
wrap_row,
|
||||||
TransformBlock::ExcerptFooter {
|
Block::ExcerptFooter {
|
||||||
id: prev.id,
|
id: prev.id,
|
||||||
height: excerpt_footer_height,
|
height: excerpt_footer_height,
|
||||||
disposition: if excerpt_boundary.next.is_some() {
|
disposition,
|
||||||
BlockDisposition::Above
|
|
||||||
} else {
|
|
||||||
BlockDisposition::Below
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -596,7 +619,7 @@ impl BlockMap {
|
||||||
|
|
||||||
(
|
(
|
||||||
wrap_row,
|
wrap_row,
|
||||||
TransformBlock::ExcerptHeader {
|
Block::ExcerptHeader {
|
||||||
id: next.id,
|
id: next.id,
|
||||||
buffer: next.buffer,
|
buffer: next.buffer,
|
||||||
range: next.range,
|
range: next.range,
|
||||||
|
@ -692,7 +715,7 @@ impl<'a> DerefMut for BlockMapReader<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BlockMapReader<'a> {
|
impl<'a> BlockMapReader<'a> {
|
||||||
pub fn row_for_block(&self, block_id: BlockId) -> Option<BlockRow> {
|
pub fn row_for_block(&self, block_id: CustomBlockId) -> Option<BlockRow> {
|
||||||
let block = self.blocks.iter().find(|block| block.id == block_id)?;
|
let block = self.blocks.iter().find(|block| block.id == block_id)?;
|
||||||
let buffer_row = block
|
let buffer_row = block
|
||||||
.position
|
.position
|
||||||
|
@ -737,14 +760,14 @@ impl<'a> BlockMapWriter<'a> {
|
||||||
pub fn insert(
|
pub fn insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
|
blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
|
||||||
) -> Vec<BlockId> {
|
) -> Vec<CustomBlockId> {
|
||||||
let mut ids = Vec::new();
|
let mut ids = Vec::new();
|
||||||
let mut edits = Patch::default();
|
let mut edits = Patch::default();
|
||||||
let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
|
let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
|
||||||
let buffer = wrap_snapshot.buffer_snapshot();
|
let buffer = wrap_snapshot.buffer_snapshot();
|
||||||
|
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
let id = BlockId(self.0.next_block_id.fetch_add(1, SeqCst));
|
let id = CustomBlockId(self.0.next_block_id.fetch_add(1, SeqCst));
|
||||||
ids.push(id);
|
ids.push(id);
|
||||||
|
|
||||||
let position = block.position;
|
let position = block.position;
|
||||||
|
@ -759,22 +782,21 @@ impl<'a> BlockMapWriter<'a> {
|
||||||
|
|
||||||
let block_ix = match self
|
let block_ix = match self
|
||||||
.0
|
.0
|
||||||
.blocks
|
.custom_blocks
|
||||||
.binary_search_by(|probe| probe.position.cmp(&position, buffer))
|
.binary_search_by(|probe| probe.position.cmp(&position, buffer))
|
||||||
{
|
{
|
||||||
Ok(ix) | Err(ix) => ix,
|
Ok(ix) | Err(ix) => ix,
|
||||||
};
|
};
|
||||||
self.0.blocks.insert(
|
let new_block = Arc::new(CustomBlock {
|
||||||
block_ix,
|
id,
|
||||||
Arc::new(Block {
|
position,
|
||||||
id,
|
height: block.height,
|
||||||
position,
|
render: Mutex::new(block.render),
|
||||||
height: block.height,
|
disposition: block.disposition,
|
||||||
render: Mutex::new(block.render),
|
style: block.style,
|
||||||
disposition: block.disposition,
|
});
|
||||||
style: block.style,
|
self.0.custom_blocks.insert(block_ix, new_block.clone());
|
||||||
}),
|
self.0.custom_blocks_by_id.insert(id, new_block);
|
||||||
);
|
|
||||||
|
|
||||||
edits = edits.compose([Edit {
|
edits = edits.compose([Edit {
|
||||||
old: start_row..end_row,
|
old: start_row..end_row,
|
||||||
|
@ -786,16 +808,19 @@ impl<'a> BlockMapWriter<'a> {
|
||||||
ids
|
ids
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace(&mut self, mut heights_and_renderers: HashMap<BlockId, (u8, RenderBlock)>) {
|
pub fn replace(
|
||||||
|
&mut self,
|
||||||
|
mut heights_and_renderers: HashMap<CustomBlockId, (u8, RenderBlock)>,
|
||||||
|
) {
|
||||||
let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
|
let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
|
||||||
let buffer = wrap_snapshot.buffer_snapshot();
|
let buffer = wrap_snapshot.buffer_snapshot();
|
||||||
let mut edits = Patch::default();
|
let mut edits = Patch::default();
|
||||||
let mut last_block_buffer_row = None;
|
let mut last_block_buffer_row = None;
|
||||||
|
|
||||||
for block in &mut self.0.blocks {
|
for block in &mut self.0.custom_blocks {
|
||||||
if let Some((new_height, render)) = heights_and_renderers.remove(&block.id) {
|
if let Some((new_height, render)) = heights_and_renderers.remove(&block.id) {
|
||||||
if block.height != new_height {
|
if block.height != new_height {
|
||||||
let new_block = Block {
|
let new_block = CustomBlock {
|
||||||
id: block.id,
|
id: block.id,
|
||||||
position: block.position,
|
position: block.position,
|
||||||
height: new_height,
|
height: new_height,
|
||||||
|
@ -803,7 +828,9 @@ impl<'a> BlockMapWriter<'a> {
|
||||||
render: Mutex::new(render),
|
render: Mutex::new(render),
|
||||||
disposition: block.disposition,
|
disposition: block.disposition,
|
||||||
};
|
};
|
||||||
*block = Arc::new(new_block);
|
let new_block = Arc::new(new_block);
|
||||||
|
*block = new_block.clone();
|
||||||
|
self.0.custom_blocks_by_id.insert(block.id, new_block);
|
||||||
|
|
||||||
let buffer_row = block.position.to_point(buffer).row;
|
let buffer_row = block.position.to_point(buffer).row;
|
||||||
if last_block_buffer_row != Some(buffer_row) {
|
if last_block_buffer_row != Some(buffer_row) {
|
||||||
|
@ -828,12 +855,12 @@ impl<'a> BlockMapWriter<'a> {
|
||||||
self.0.sync(wrap_snapshot, edits);
|
self.0.sync(wrap_snapshot, edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, block_ids: HashSet<BlockId>) {
|
pub fn remove(&mut self, block_ids: HashSet<CustomBlockId>) {
|
||||||
let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
|
let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
|
||||||
let buffer = wrap_snapshot.buffer_snapshot();
|
let buffer = wrap_snapshot.buffer_snapshot();
|
||||||
let mut edits = Patch::default();
|
let mut edits = Patch::default();
|
||||||
let mut last_block_buffer_row = None;
|
let mut last_block_buffer_row = None;
|
||||||
self.0.blocks.retain(|block| {
|
self.0.custom_blocks.retain(|block| {
|
||||||
if block_ids.contains(&block.id) {
|
if block_ids.contains(&block.id) {
|
||||||
let buffer_row = block.position.to_point(buffer).row;
|
let buffer_row = block.position.to_point(buffer).row;
|
||||||
if last_block_buffer_row != Some(buffer_row) {
|
if last_block_buffer_row != Some(buffer_row) {
|
||||||
|
@ -850,6 +877,7 @@ impl<'a> BlockMapWriter<'a> {
|
||||||
new: start_row..end_row,
|
new: start_row..end_row,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
self.0.custom_blocks_by_id.remove(&block.id);
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -934,10 +962,7 @@ impl BlockSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blocks_in_range(
|
pub fn blocks_in_range(&self, rows: Range<u32>) -> impl Iterator<Item = (u32, &Block)> {
|
||||||
&self,
|
|
||||||
rows: Range<u32>,
|
|
||||||
) -> impl Iterator<Item = (u32, &TransformBlock)> {
|
|
||||||
let mut cursor = self.transforms.cursor::<BlockRow>();
|
let mut cursor = self.transforms.cursor::<BlockRow>();
|
||||||
cursor.seek(&BlockRow(rows.start), Bias::Right, &());
|
cursor.seek(&BlockRow(rows.start), Bias::Right, &());
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
|
@ -957,6 +982,60 @@ impl BlockSnapshot {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
|
||||||
|
let buffer = self.wrap_snapshot.buffer_snapshot();
|
||||||
|
|
||||||
|
match block_id {
|
||||||
|
BlockId::Custom(custom_block_id) => {
|
||||||
|
let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
|
||||||
|
Some(Block::Custom(custom_block.clone()))
|
||||||
|
}
|
||||||
|
BlockId::ExcerptHeader(excerpt_id) => {
|
||||||
|
let excerpt_range = buffer.range_for_excerpt::<Point>(excerpt_id)?;
|
||||||
|
let wrap_point = self
|
||||||
|
.wrap_snapshot
|
||||||
|
.make_wrap_point(excerpt_range.start, Bias::Left);
|
||||||
|
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
|
||||||
|
cursor.seek(&WrapRow(wrap_point.row()), Bias::Left, &());
|
||||||
|
while let Some(transform) = cursor.item() {
|
||||||
|
if let Some(block) = transform.block.as_ref() {
|
||||||
|
if block.id() == block_id {
|
||||||
|
return Some(block.clone());
|
||||||
|
}
|
||||||
|
} else if cursor.start().0 > WrapRow(wrap_point.row()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.next(&());
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
BlockId::ExcerptFooter(excerpt_id) => {
|
||||||
|
let excerpt_range = buffer.range_for_excerpt::<Point>(excerpt_id)?;
|
||||||
|
let wrap_point = self
|
||||||
|
.wrap_snapshot
|
||||||
|
.make_wrap_point(excerpt_range.end, Bias::Left);
|
||||||
|
|
||||||
|
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
|
||||||
|
cursor.seek(&WrapRow(wrap_point.row()), Bias::Left, &());
|
||||||
|
while let Some(transform) = cursor.item() {
|
||||||
|
if let Some(block) = transform.block.as_ref() {
|
||||||
|
if block.id() == block_id {
|
||||||
|
return Some(block.clone());
|
||||||
|
}
|
||||||
|
} else if cursor.start().0 > WrapRow(wrap_point.row()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.next(&());
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn max_point(&self) -> BlockPoint {
|
pub fn max_point(&self) -> BlockPoint {
|
||||||
let row = self.transforms.summary().output_rows - 1;
|
let row = self.transforms.summary().output_rows - 1;
|
||||||
BlockPoint::new(row, self.line_len(BlockRow(row)))
|
BlockPoint::new(row, self.line_len(BlockRow(row)))
|
||||||
|
@ -1086,7 +1165,7 @@ impl Transform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block(block: TransformBlock) -> Self {
|
fn block(block: Block) -> Self {
|
||||||
Self {
|
Self {
|
||||||
summary: TransformSummary {
|
summary: TransformSummary {
|
||||||
input_rows: 0,
|
input_rows: 0,
|
||||||
|
@ -1235,7 +1314,7 @@ impl DerefMut for BlockContext<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Block {
|
impl CustomBlock {
|
||||||
pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
|
pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
|
||||||
self.render.lock()(cx)
|
self.render.lock()(cx)
|
||||||
}
|
}
|
||||||
|
@ -1249,7 +1328,7 @@ impl Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Block {
|
impl Debug for CustomBlock {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Block")
|
f.debug_struct("Block")
|
||||||
.field("id", &self.id)
|
.field("id", &self.id)
|
||||||
|
@ -1279,15 +1358,16 @@ fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::display_map::inlay_map::InlayMap;
|
use crate::display_map::{
|
||||||
use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
|
fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
|
||||||
use gpui::{div, font, px, Element};
|
};
|
||||||
|
use gpui::{div, font, px, AppContext, Context as _, Element};
|
||||||
|
use language::{Buffer, Capability};
|
||||||
use multi_buffer::MultiBuffer;
|
use multi_buffer::MultiBuffer;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
|
use std::env;
|
||||||
use util::RandomCharIter;
|
use util::RandomCharIter;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -1474,6 +1554,89 @@ mod tests {
|
||||||
assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
|
assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_multibuffer_headers_and_footers(cx: &mut AppContext) {
|
||||||
|
init_test(cx);
|
||||||
|
|
||||||
|
let buffer1 = cx.new_model(|cx| Buffer::local("Buffer 1", cx));
|
||||||
|
let buffer2 = cx.new_model(|cx| Buffer::local("Buffer 2", cx));
|
||||||
|
let buffer3 = cx.new_model(|cx| Buffer::local("Buffer 3", cx));
|
||||||
|
|
||||||
|
let mut excerpt_ids = Vec::new();
|
||||||
|
let multi_buffer = cx.new_model(|cx| {
|
||||||
|
let mut multi_buffer = MultiBuffer::new(0, Capability::ReadWrite);
|
||||||
|
excerpt_ids.extend(multi_buffer.push_excerpts(
|
||||||
|
buffer1.clone(),
|
||||||
|
[ExcerptRange {
|
||||||
|
context: 0..buffer1.read(cx).len(),
|
||||||
|
primary: None,
|
||||||
|
}],
|
||||||
|
cx,
|
||||||
|
));
|
||||||
|
excerpt_ids.extend(multi_buffer.push_excerpts(
|
||||||
|
buffer2.clone(),
|
||||||
|
[ExcerptRange {
|
||||||
|
context: 0..buffer2.read(cx).len(),
|
||||||
|
primary: None,
|
||||||
|
}],
|
||||||
|
cx,
|
||||||
|
));
|
||||||
|
excerpt_ids.extend(multi_buffer.push_excerpts(
|
||||||
|
buffer3.clone(),
|
||||||
|
[ExcerptRange {
|
||||||
|
context: 0..buffer3.read(cx).len(),
|
||||||
|
primary: None,
|
||||||
|
}],
|
||||||
|
cx,
|
||||||
|
));
|
||||||
|
|
||||||
|
multi_buffer
|
||||||
|
});
|
||||||
|
|
||||||
|
let font = font("Helvetica");
|
||||||
|
let font_size = px(14.);
|
||||||
|
let font_id = cx.text_system().resolve_font(&font);
|
||||||
|
let mut wrap_width = px(0.);
|
||||||
|
for c in "Buff".chars() {
|
||||||
|
wrap_width += cx
|
||||||
|
.text_system()
|
||||||
|
.advance(font_id, font_size, c)
|
||||||
|
.unwrap()
|
||||||
|
.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
|
||||||
|
let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone());
|
||||||
|
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
|
||||||
|
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
|
||||||
|
let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
|
||||||
|
|
||||||
|
let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
|
||||||
|
let snapshot = block_map.read(wraps_snapshot, Default::default());
|
||||||
|
|
||||||
|
// Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
|
||||||
|
assert_eq!(
|
||||||
|
snapshot.text(),
|
||||||
|
"\nBuff\ner 1\n\n\nBuff\ner 2\n\n\nBuff\ner 3\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
let blocks: Vec<_> = snapshot
|
||||||
|
.blocks_in_range(0..u32::MAX)
|
||||||
|
.map(|(row, block)| (row, block.id()))
|
||||||
|
.collect();
|
||||||
|
assert_eq!(
|
||||||
|
blocks,
|
||||||
|
vec![
|
||||||
|
(0, BlockId::ExcerptHeader(excerpt_ids[0])),
|
||||||
|
(3, BlockId::ExcerptFooter(excerpt_ids[0])),
|
||||||
|
(4, BlockId::ExcerptHeader(excerpt_ids[1])),
|
||||||
|
(7, BlockId::ExcerptFooter(excerpt_ids[1])),
|
||||||
|
(8, BlockId::ExcerptHeader(excerpt_ids[2])),
|
||||||
|
(11, BlockId::ExcerptFooter(excerpt_ids[2]))
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
|
fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
|
||||||
let _update = cx.update(|cx| init_test(cx));
|
let _update = cx.update(|cx| init_test(cx));
|
||||||
|
@ -1807,7 +1970,7 @@ mod tests {
|
||||||
|
|
||||||
// Note that this needs to be synced with the related section in BlockMap::sync
|
// Note that this needs to be synced with the related section in BlockMap::sync
|
||||||
expected_blocks.extend(
|
expected_blocks.extend(
|
||||||
BlockMap::header_blocks(
|
BlockMap::header_and_footer_blocks(
|
||||||
true,
|
true,
|
||||||
excerpt_footer_height,
|
excerpt_footer_height,
|
||||||
buffer_start_header_height,
|
buffer_start_header_height,
|
||||||
|
@ -1911,6 +2074,16 @@ mod tests {
|
||||||
expected_block_positions
|
expected_block_positions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
for (_, expected_block) in
|
||||||
|
blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
|
||||||
|
{
|
||||||
|
let actual_block = blocks_snapshot.block_for_id(expected_block.id());
|
||||||
|
assert_eq!(
|
||||||
|
actual_block.map(|block| block.id()),
|
||||||
|
Some(expected_block.id())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (block_row, block) in expected_block_positions {
|
for (block_row, block) in expected_block_positions {
|
||||||
if let BlockType::Custom(block_id) = block.block_type() {
|
if let BlockType::Custom(block_id) = block.block_type() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -2007,7 +2180,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
Custom {
|
Custom {
|
||||||
disposition: BlockDisposition,
|
disposition: BlockDisposition,
|
||||||
id: BlockId,
|
id: CustomBlockId,
|
||||||
height: u8,
|
height: u8,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -2044,15 +2217,15 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TransformBlock> for ExpectedBlock {
|
impl From<Block> for ExpectedBlock {
|
||||||
fn from(block: TransformBlock) -> Self {
|
fn from(block: Block) -> Self {
|
||||||
match block {
|
match block {
|
||||||
TransformBlock::Custom(block) => ExpectedBlock::Custom {
|
Block::Custom(block) => ExpectedBlock::Custom {
|
||||||
id: block.id,
|
id: block.id,
|
||||||
disposition: block.disposition,
|
disposition: block.disposition,
|
||||||
height: block.height,
|
height: block.height,
|
||||||
},
|
},
|
||||||
TransformBlock::ExcerptHeader {
|
Block::ExcerptHeader {
|
||||||
height,
|
height,
|
||||||
starts_new_buffer,
|
starts_new_buffer,
|
||||||
..
|
..
|
||||||
|
@ -2060,7 +2233,7 @@ mod tests {
|
||||||
height,
|
height,
|
||||||
starts_new_buffer,
|
starts_new_buffer,
|
||||||
},
|
},
|
||||||
TransformBlock::ExcerptFooter {
|
Block::ExcerptFooter {
|
||||||
height,
|
height,
|
||||||
disposition,
|
disposition,
|
||||||
..
|
..
|
||||||
|
@ -2080,12 +2253,12 @@ mod tests {
|
||||||
assets::Assets.load_test_fonts(cx);
|
assets::Assets.load_test_fonts(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformBlock {
|
impl Block {
|
||||||
fn as_custom(&self) -> Option<&Block> {
|
fn as_custom(&self) -> Option<&CustomBlock> {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom(block) => Some(block),
|
Block::Custom(block) => Some(block),
|
||||||
TransformBlock::ExcerptHeader { .. } => None,
|
Block::ExcerptHeader { .. } => None,
|
||||||
TransformBlock::ExcerptFooter { .. } => None,
|
Block::ExcerptFooter { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -568,6 +568,7 @@ pub struct Editor {
|
||||||
previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
|
previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
|
||||||
file_header_size: u8,
|
file_header_size: u8,
|
||||||
breadcrumb_header: Option<String>,
|
breadcrumb_header: Option<String>,
|
||||||
|
focused_block: Option<FocusedBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -785,7 +786,7 @@ pub struct RenameState {
|
||||||
pub range: Range<Anchor>,
|
pub range: Range<Anchor>,
|
||||||
pub old_name: Arc<str>,
|
pub old_name: Arc<str>,
|
||||||
pub editor: View<Editor>,
|
pub editor: View<Editor>,
|
||||||
block_id: BlockId,
|
block_id: CustomBlockId,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InvalidationStack<T>(Vec<T>);
|
struct InvalidationStack<T>(Vec<T>);
|
||||||
|
@ -1537,7 +1538,7 @@ struct ActiveDiagnosticGroup {
|
||||||
primary_range: Range<Anchor>,
|
primary_range: Range<Anchor>,
|
||||||
primary_message: String,
|
primary_message: String,
|
||||||
group_id: usize,
|
group_id: usize,
|
||||||
blocks: HashMap<BlockId, Diagnostic>,
|
blocks: HashMap<CustomBlockId, Diagnostic>,
|
||||||
is_valid: bool,
|
is_valid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1585,6 +1586,11 @@ impl InlayHintRefreshReason {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct FocusedBlock {
|
||||||
|
id: BlockId,
|
||||||
|
focus_handle: WeakFocusHandle,
|
||||||
|
}
|
||||||
|
|
||||||
impl Editor {
|
impl Editor {
|
||||||
pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
|
pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
|
||||||
let buffer = cx.new_model(|cx| Buffer::local("", cx));
|
let buffer = cx.new_model(|cx| Buffer::local("", cx));
|
||||||
|
@ -1908,6 +1914,7 @@ impl Editor {
|
||||||
linked_edit_ranges: Default::default(),
|
linked_edit_ranges: Default::default(),
|
||||||
previous_search_ranges: None,
|
previous_search_ranges: None,
|
||||||
breadcrumb_header: None,
|
breadcrumb_header: None,
|
||||||
|
focused_block: None,
|
||||||
};
|
};
|
||||||
this.tasks_update_task = Some(this.refresh_runnables(cx));
|
this.tasks_update_task = Some(this.refresh_runnables(cx));
|
||||||
this._subscriptions.extend(project_subscriptions);
|
this._subscriptions.extend(project_subscriptions);
|
||||||
|
@ -10150,7 +10157,7 @@ impl Editor {
|
||||||
blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
|
blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
|
||||||
autoscroll: Option<Autoscroll>,
|
autoscroll: Option<Autoscroll>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Vec<BlockId> {
|
) -> Vec<CustomBlockId> {
|
||||||
let blocks = self
|
let blocks = self
|
||||||
.display_map
|
.display_map
|
||||||
.update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
|
.update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
|
||||||
|
@ -10162,7 +10169,7 @@ impl Editor {
|
||||||
|
|
||||||
pub fn replace_blocks(
|
pub fn replace_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
blocks: HashMap<BlockId, (Option<u8>, RenderBlock)>,
|
blocks: HashMap<CustomBlockId, (Option<u8>, RenderBlock)>,
|
||||||
autoscroll: Option<Autoscroll>,
|
autoscroll: Option<Autoscroll>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
|
@ -10175,7 +10182,7 @@ impl Editor {
|
||||||
|
|
||||||
pub fn remove_blocks(
|
pub fn remove_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
block_ids: HashSet<BlockId>,
|
block_ids: HashSet<CustomBlockId>,
|
||||||
autoscroll: Option<Autoscroll>,
|
autoscroll: Option<Autoscroll>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
|
@ -10189,13 +10196,21 @@ impl Editor {
|
||||||
|
|
||||||
pub fn row_for_block(
|
pub fn row_for_block(
|
||||||
&self,
|
&self,
|
||||||
block_id: BlockId,
|
block_id: CustomBlockId,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<DisplayRow> {
|
) -> Option<DisplayRow> {
|
||||||
self.display_map
|
self.display_map
|
||||||
.update(cx, |map, cx| map.row_for_block(block_id, cx))
|
.update(cx, |map, cx| map.row_for_block(block_id, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
|
||||||
|
self.focused_block = Some(focused_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
|
||||||
|
self.focused_block.take()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_creases(
|
pub fn insert_creases(
|
||||||
&mut self,
|
&mut self,
|
||||||
creases: impl IntoIterator<Item = Crease>,
|
creases: impl IntoIterator<Item = Crease>,
|
||||||
|
@ -12830,7 +12845,7 @@ pub fn diagnostic_block_renderer(
|
||||||
highlight_diagnostic_message(&diagnostic, max_message_rows);
|
highlight_diagnostic_message(&diagnostic, max_message_rows);
|
||||||
|
|
||||||
Box::new(move |cx: &mut BlockContext| {
|
Box::new(move |cx: &mut BlockContext| {
|
||||||
let group_id: SharedString = cx.transform_block_id.to_string().into();
|
let group_id: SharedString = cx.block_id.to_string().into();
|
||||||
|
|
||||||
let mut text_style = cx.text_style().clone();
|
let mut text_style = cx.text_style().clone();
|
||||||
text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
|
text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
|
||||||
|
@ -12842,7 +12857,7 @@ pub fn diagnostic_block_renderer(
|
||||||
|
|
||||||
let multi_line_diagnostic = diagnostic.message.contains('\n');
|
let multi_line_diagnostic = diagnostic.message.contains('\n');
|
||||||
|
|
||||||
let buttons = |diagnostic: &Diagnostic, block_id: TransformBlockId| {
|
let buttons = |diagnostic: &Diagnostic, block_id: BlockId| {
|
||||||
if multi_line_diagnostic {
|
if multi_line_diagnostic {
|
||||||
v_flex()
|
v_flex()
|
||||||
} else {
|
} else {
|
||||||
|
@ -12873,12 +12888,12 @@ pub fn diagnostic_block_renderer(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let icon_size = buttons(&diagnostic, cx.transform_block_id)
|
let icon_size = buttons(&diagnostic, cx.block_id)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
.layout_as_root(AvailableSpace::min_size(), cx);
|
.layout_as_root(AvailableSpace::min_size(), cx);
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.id(cx.transform_block_id)
|
.id(cx.block_id)
|
||||||
.group(group_id.clone())
|
.group(group_id.clone())
|
||||||
.relative()
|
.relative()
|
||||||
.size_full()
|
.size_full()
|
||||||
|
@ -12890,7 +12905,7 @@ pub fn diagnostic_block_renderer(
|
||||||
.w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
|
.w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
|
||||||
.flex_shrink(),
|
.flex_shrink(),
|
||||||
)
|
)
|
||||||
.child(buttons(&diagnostic, cx.transform_block_id))
|
.child(buttons(&diagnostic, cx.block_id))
|
||||||
.child(div().flex().flex_shrink_0().child(
|
.child(div().flex().flex_shrink_0().child(
|
||||||
StyledText::new(text_without_backticks.clone()).with_highlights(
|
StyledText::new(text_without_backticks.clone()).with_highlights(
|
||||||
&text_style,
|
&text_style,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,7 +23,7 @@ use crate::{
|
||||||
git::{diff_hunk_to_display, DisplayDiffHunk},
|
git::{diff_hunk_to_display, DisplayDiffHunk},
|
||||||
hunk_status, hunks_for_selections,
|
hunk_status, hunks_for_selections,
|
||||||
mouse_context_menu::MouseContextMenu,
|
mouse_context_menu::MouseContextMenu,
|
||||||
BlockDisposition, BlockId, BlockProperties, BlockStyle, DiffRowHighlight, Editor,
|
BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, Editor,
|
||||||
EditorSnapshot, ExpandAllHunkDiffs, RangeToAnchorExt, RevertSelectedHunks, ToDisplayPoint,
|
EditorSnapshot, ExpandAllHunkDiffs, RangeToAnchorExt, RevertSelectedHunks, ToDisplayPoint,
|
||||||
ToggleHunkDiff,
|
ToggleHunkDiff,
|
||||||
};
|
};
|
||||||
|
@ -58,7 +58,7 @@ impl ExpandedHunks {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct ExpandedHunk {
|
pub(super) struct ExpandedHunk {
|
||||||
pub block: Option<BlockId>,
|
pub block: Option<CustomBlockId>,
|
||||||
pub hunk_range: Range<Anchor>,
|
pub hunk_range: Range<Anchor>,
|
||||||
pub diff_base_byte_range: Range<usize>,
|
pub diff_base_byte_range: Range<usize>,
|
||||||
pub status: DiffHunkStatus,
|
pub status: DiffHunkStatus,
|
||||||
|
@ -425,7 +425,7 @@ impl Editor {
|
||||||
deleted_text_height: u8,
|
deleted_text_height: u8,
|
||||||
hunk: &HoveredHunk,
|
hunk: &HoveredHunk,
|
||||||
cx: &mut ViewContext<'_, Self>,
|
cx: &mut ViewContext<'_, Self>,
|
||||||
) -> Option<BlockId> {
|
) -> Option<CustomBlockId> {
|
||||||
let deleted_hunk_color = deleted_hunk_color(cx);
|
let deleted_hunk_color = deleted_hunk_color(cx);
|
||||||
let (editor_height, editor_with_deleted_text) =
|
let (editor_height, editor_with_deleted_text) =
|
||||||
editor_with_deleted_text(diff_base_buffer, deleted_hunk_color, hunk, cx);
|
editor_with_deleted_text(diff_base_buffer, deleted_hunk_color, hunk, cx);
|
||||||
|
|
|
@ -32,8 +32,8 @@
|
||||||
//! your own custom layout algorithm or rendering a code editor.
|
//! your own custom layout algorithm or rendering a code editor.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementId, LayoutId,
|
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementId, FocusHandle,
|
||||||
Pixels, Point, Size, Style, ViewContext, WindowContext, ELEMENT_ARENA,
|
LayoutId, Pixels, Point, Size, Style, ViewContext, WindowContext, ELEMENT_ARENA,
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
pub(crate) use smallvec::SmallVec;
|
pub(crate) use smallvec::SmallVec;
|
||||||
|
@ -209,7 +209,7 @@ impl<C: RenderOnce> Element for Component<C> {
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
element.paint(cx)
|
element.paint(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,13 +493,23 @@ impl AnyElement {
|
||||||
|
|
||||||
/// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and
|
/// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and
|
||||||
/// request autoscroll before the final paint pass is confirmed.
|
/// request autoscroll before the final paint pass is confirmed.
|
||||||
pub fn prepaint(&mut self, cx: &mut WindowContext) {
|
pub fn prepaint(&mut self, cx: &mut WindowContext) -> Option<FocusHandle> {
|
||||||
self.0.prepaint(cx)
|
let focus_assigned = cx.window.next_frame.focus.is_some();
|
||||||
|
|
||||||
|
self.0.prepaint(cx);
|
||||||
|
|
||||||
|
if !focus_assigned {
|
||||||
|
if let Some(focus_id) = cx.window.next_frame.focus {
|
||||||
|
return FocusHandle::for_id(focus_id, &cx.window.focus_handles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paints the element stored in this `AnyElement`.
|
/// Paints the element stored in this `AnyElement`.
|
||||||
pub fn paint(&mut self, cx: &mut WindowContext) {
|
pub fn paint(&mut self, cx: &mut WindowContext) {
|
||||||
self.0.paint(cx)
|
self.0.paint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs layout for this element within the given available space and returns its size.
|
/// Performs layout for this element within the given available space and returns its size.
|
||||||
|
@ -512,19 +522,25 @@ impl AnyElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepaints this element at the given absolute origin.
|
/// Prepaints this element at the given absolute origin.
|
||||||
pub fn prepaint_at(&mut self, origin: Point<Pixels>, cx: &mut WindowContext) {
|
/// If any element in the subtree beneath this element is focused, its FocusHandle is returned.
|
||||||
cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
|
pub fn prepaint_at(
|
||||||
|
&mut self,
|
||||||
|
origin: Point<Pixels>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> Option<FocusHandle> {
|
||||||
|
cx.with_absolute_element_offset(origin, |cx| self.prepaint(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs layout on this element in the available space, then prepaints it at the given absolute origin.
|
/// Performs layout on this element in the available space, then prepaints it at the given absolute origin.
|
||||||
|
/// If any element in the subtree beneath this element is focused, its FocusHandle is returned.
|
||||||
pub fn prepaint_as_root(
|
pub fn prepaint_as_root(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: Point<Pixels>,
|
origin: Point<Pixels>,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) -> Option<FocusHandle> {
|
||||||
self.layout_as_root(available_space, cx);
|
self.layout_as_root(available_space, cx);
|
||||||
cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
|
cx.with_absolute_element_offset(origin, |cx| self.prepaint(cx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,7 +568,7 @@ impl Element for AnyElement {
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
self.prepaint(cx)
|
self.prepaint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
|
@ -563,7 +579,7 @@ impl Element for AnyElement {
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
self.paint(cx)
|
self.paint(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1359,6 +1359,9 @@ impl Interactivity {
|
||||||
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut WindowContext) -> R,
|
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut WindowContext) -> R,
|
||||||
) -> R {
|
) -> R {
|
||||||
self.content_size = content_size;
|
self.content_size = content_size;
|
||||||
|
if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
|
||||||
|
cx.set_focus_handle(&focus_handle);
|
||||||
|
}
|
||||||
cx.with_optional_element_state::<InteractiveElementState, _>(
|
cx.with_optional_element_state::<InteractiveElementState, _>(
|
||||||
global_id,
|
global_id,
|
||||||
|element_state, cx| {
|
|element_state, cx| {
|
||||||
|
@ -1998,9 +2001,6 @@ impl Interactivity {
|
||||||
if let Some(context) = self.key_context.clone() {
|
if let Some(context) = self.key_context.clone() {
|
||||||
cx.set_key_context(context);
|
cx.set_key_context(context);
|
||||||
}
|
}
|
||||||
if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
|
|
||||||
cx.set_focus_handle(focus_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
for listener in key_down_listeners {
|
for listener in key_down_listeners {
|
||||||
cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
|
cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
|
||||||
|
|
|
@ -92,6 +92,7 @@ pub(crate) struct DispatchNode {
|
||||||
pub(crate) struct ReusedSubtree {
|
pub(crate) struct ReusedSubtree {
|
||||||
old_range: Range<usize>,
|
old_range: Range<usize>,
|
||||||
new_range: Range<usize>,
|
new_range: Range<usize>,
|
||||||
|
contains_focus: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReusedSubtree {
|
impl ReusedSubtree {
|
||||||
|
@ -104,6 +105,10 @@ impl ReusedSubtree {
|
||||||
);
|
);
|
||||||
DispatchNodeId((node_id.0 - self.old_range.start) + self.new_range.start)
|
DispatchNodeId((node_id.0 - self.old_range.start) + self.new_range.start)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains_focus(&self) -> bool {
|
||||||
|
self.contains_focus
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
|
type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
|
||||||
|
@ -246,9 +251,15 @@ impl DispatchTree {
|
||||||
target.modifiers_changed_listeners = mem::take(&mut source.modifiers_changed_listeners);
|
target.modifiers_changed_listeners = mem::take(&mut source.modifiers_changed_listeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reuse_subtree(&mut self, old_range: Range<usize>, source: &mut Self) -> ReusedSubtree {
|
pub fn reuse_subtree(
|
||||||
|
&mut self,
|
||||||
|
old_range: Range<usize>,
|
||||||
|
source: &mut Self,
|
||||||
|
focus: Option<FocusId>,
|
||||||
|
) -> ReusedSubtree {
|
||||||
let new_range = self.nodes.len()..self.nodes.len() + old_range.len();
|
let new_range = self.nodes.len()..self.nodes.len() + old_range.len();
|
||||||
|
|
||||||
|
let mut contains_focus = false;
|
||||||
let mut source_stack = vec![];
|
let mut source_stack = vec![];
|
||||||
for (source_node_id, source_node) in source
|
for (source_node_id, source_node) in source
|
||||||
.nodes
|
.nodes
|
||||||
|
@ -268,6 +279,9 @@ impl DispatchTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
source_stack.push(source_node_id);
|
source_stack.push(source_node_id);
|
||||||
|
if source_node.focus_id.is_some() && source_node.focus_id == focus {
|
||||||
|
contains_focus = true;
|
||||||
|
}
|
||||||
self.move_node(source_node);
|
self.move_node(source_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +293,7 @@ impl DispatchTree {
|
||||||
ReusedSubtree {
|
ReusedSubtree {
|
||||||
old_range,
|
old_range,
|
||||||
new_range,
|
new_range,
|
||||||
|
contains_focus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -464,6 +464,7 @@ impl Frame {
|
||||||
self.cursor_styles.clear();
|
self.cursor_styles.clear();
|
||||||
self.hitboxes.clear();
|
self.hitboxes.clear();
|
||||||
self.deferred_draws.clear();
|
self.deferred_draws.clear();
|
||||||
|
self.focus = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
|
pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
|
||||||
|
@ -1460,7 +1461,6 @@ impl<'a> WindowContext<'a> {
|
||||||
&mut self.window.rendered_frame.dispatch_tree,
|
&mut self.window.rendered_frame.dispatch_tree,
|
||||||
self.window.focus,
|
self.window.focus,
|
||||||
);
|
);
|
||||||
self.window.next_frame.focus = self.window.focus;
|
|
||||||
self.window.next_frame.window_active = self.window.active.get();
|
self.window.next_frame.window_active = self.window.active.get();
|
||||||
|
|
||||||
// Register requested input handler with the platform window.
|
// Register requested input handler with the platform window.
|
||||||
|
@ -1574,7 +1574,7 @@ impl<'a> WindowContext<'a> {
|
||||||
self.paint_deferred_draws(&sorted_deferred_draws);
|
self.paint_deferred_draws(&sorted_deferred_draws);
|
||||||
|
|
||||||
if let Some(mut prompt_element) = prompt_element {
|
if let Some(mut prompt_element) = prompt_element {
|
||||||
prompt_element.paint(self)
|
prompt_element.paint(self);
|
||||||
} else if let Some(mut drag_element) = active_drag_element {
|
} else if let Some(mut drag_element) = active_drag_element {
|
||||||
drag_element.paint(self);
|
drag_element.paint(self);
|
||||||
} else if let Some(mut tooltip_element) = tooltip_element {
|
} else if let Some(mut tooltip_element) = tooltip_element {
|
||||||
|
@ -1730,7 +1730,13 @@ impl<'a> WindowContext<'a> {
|
||||||
let reused_subtree = window.next_frame.dispatch_tree.reuse_subtree(
|
let reused_subtree = window.next_frame.dispatch_tree.reuse_subtree(
|
||||||
range.start.dispatch_tree_index..range.end.dispatch_tree_index,
|
range.start.dispatch_tree_index..range.end.dispatch_tree_index,
|
||||||
&mut window.rendered_frame.dispatch_tree,
|
&mut window.rendered_frame.dispatch_tree,
|
||||||
|
window.focus,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if reused_subtree.contains_focus() {
|
||||||
|
window.next_frame.focus = window.focus;
|
||||||
|
}
|
||||||
|
|
||||||
window.next_frame.deferred_draws.extend(
|
window.next_frame.deferred_draws.extend(
|
||||||
window.rendered_frame.deferred_draws
|
window.rendered_frame.deferred_draws
|
||||||
[range.start.deferred_draws_index..range.end.deferred_draws_index]
|
[range.start.deferred_draws_index..range.end.deferred_draws_index]
|
||||||
|
@ -2845,13 +2851,16 @@ impl<'a> WindowContext<'a> {
|
||||||
/// Sets the focus handle for the current element. This handle will be used to manage focus state
|
/// Sets the focus handle for the current element. This handle will be used to manage focus state
|
||||||
/// and keyboard event dispatch for the element.
|
/// and keyboard event dispatch for the element.
|
||||||
///
|
///
|
||||||
/// This method should only be called as part of the paint phase of element drawing.
|
/// This method should only be called as part of the prepaint phase of element drawing.
|
||||||
pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle) {
|
pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle) {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.window.draw_phase,
|
self.window.draw_phase,
|
||||||
DrawPhase::Paint,
|
DrawPhase::Prepaint,
|
||||||
"this method can only be called during paint"
|
"this method can only be called during prepaint"
|
||||||
);
|
);
|
||||||
|
if focus_handle.is_focused(self) {
|
||||||
|
self.window.next_frame.focus = Some(focus_handle.id);
|
||||||
|
}
|
||||||
self.window
|
self.window
|
||||||
.next_frame
|
.next_frame
|
||||||
.dispatch_tree
|
.dispatch_tree
|
||||||
|
|
|
@ -719,6 +719,9 @@ impl Element for MarkdownElement {
|
||||||
rendered_markdown: &mut Self::RequestLayoutState,
|
rendered_markdown: &mut Self::RequestLayoutState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Self::PrepaintState {
|
) -> Self::PrepaintState {
|
||||||
|
let focus_handle = self.markdown.read(cx).focus_handle.clone();
|
||||||
|
cx.set_focus_handle(&focus_handle);
|
||||||
|
|
||||||
let hitbox = cx.insert_hitbox(bounds, false);
|
let hitbox = cx.insert_hitbox(bounds, false);
|
||||||
rendered_markdown.element.prepaint(cx);
|
rendered_markdown.element.prepaint(cx);
|
||||||
self.autoscroll(&rendered_markdown.text, cx);
|
self.autoscroll(&rendered_markdown.text, cx);
|
||||||
|
@ -733,9 +736,6 @@ impl Element for MarkdownElement {
|
||||||
hitbox: &mut Self::PrepaintState,
|
hitbox: &mut Self::PrepaintState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
let focus_handle = self.markdown.read(cx).focus_handle.clone();
|
|
||||||
cx.set_focus_handle(&focus_handle);
|
|
||||||
|
|
||||||
let mut context = KeyContext::default();
|
let mut context = KeyContext::default();
|
||||||
context.add("Markdown");
|
context.add("Markdown");
|
||||||
cx.set_key_context(context);
|
cx.set_key_context(context);
|
||||||
|
|
|
@ -261,7 +261,7 @@ pub struct ExcerptRange<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct ExcerptSummary {
|
pub struct ExcerptSummary {
|
||||||
excerpt_id: ExcerptId,
|
excerpt_id: ExcerptId,
|
||||||
/// The location of the last [`Excerpt`] being summarized
|
/// The location of the last [`Excerpt`] being summarized
|
||||||
excerpt_locator: Locator,
|
excerpt_locator: Locator,
|
||||||
|
@ -3744,6 +3744,21 @@ impl MultiBufferSnapshot {
|
||||||
Some(&self.excerpt(excerpt_id)?.buffer)
|
Some(&self.excerpt(excerpt_id)?.buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn range_for_excerpt<'a, T: sum_tree::Dimension<'a, ExcerptSummary>>(
|
||||||
|
&'a self,
|
||||||
|
excerpt_id: ExcerptId,
|
||||||
|
) -> Option<Range<T>> {
|
||||||
|
let mut cursor = self.excerpts.cursor::<(Option<&Locator>, T)>();
|
||||||
|
let locator = self.excerpt_locator_for_id(excerpt_id);
|
||||||
|
if cursor.seek(&Some(locator), Bias::Left, &()) {
|
||||||
|
let start = cursor.start().1.clone();
|
||||||
|
let end = cursor.end(&()).1;
|
||||||
|
Some(start..end)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn excerpt(&self, excerpt_id: ExcerptId) -> Option<&Excerpt> {
|
fn excerpt(&self, excerpt_id: ExcerptId) -> Option<&Excerpt> {
|
||||||
let mut cursor = self.excerpts.cursor::<Option<&Locator>>();
|
let mut cursor = self.excerpts.cursor::<Option<&Locator>>();
|
||||||
let locator = self.excerpt_locator_for_id(excerpt_id);
|
let locator = self.excerpt_locator_for_id(excerpt_id);
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use editor::{
|
use editor::{
|
||||||
display_map::{
|
display_map::{
|
||||||
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock,
|
BlockContext, BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
|
||||||
},
|
},
|
||||||
scroll::Autoscroll,
|
scroll::Autoscroll,
|
||||||
Anchor, AnchorRangeExt as _, Editor, MultiBuffer, ToPoint,
|
Anchor, AnchorRangeExt as _, Editor, MultiBuffer, ToPoint,
|
||||||
|
@ -37,7 +37,7 @@ struct EditorBlock {
|
||||||
editor: WeakView<Editor>,
|
editor: WeakView<Editor>,
|
||||||
code_range: Range<Anchor>,
|
code_range: Range<Anchor>,
|
||||||
invalidation_anchor: Anchor,
|
invalidation_anchor: Anchor,
|
||||||
block_id: BlockId,
|
block_id: CustomBlockId,
|
||||||
execution_view: View<ExecutionView>,
|
execution_view: View<ExecutionView>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +282,7 @@ impl Session {
|
||||||
if let multi_buffer::Event::Edited { .. } = event {
|
if let multi_buffer::Event::Edited { .. } = event {
|
||||||
let snapshot = buffer.read(cx).snapshot(cx);
|
let snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
|
||||||
let mut blocks_to_remove: HashSet<BlockId> = HashSet::default();
|
let mut blocks_to_remove: HashSet<CustomBlockId> = HashSet::default();
|
||||||
|
|
||||||
self.blocks.retain(|_id, block| {
|
self.blocks.retain(|_id, block| {
|
||||||
if block.invalidation_anchor.is_valid(&snapshot) {
|
if block.invalidation_anchor.is_valid(&snapshot) {
|
||||||
|
@ -316,7 +316,7 @@ impl Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_outputs(&mut self, cx: &mut ViewContext<Self>) {
|
pub fn clear_outputs(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
let blocks_to_remove: HashSet<BlockId> =
|
let blocks_to_remove: HashSet<CustomBlockId> =
|
||||||
self.blocks.values().map(|block| block.block_id).collect();
|
self.blocks.values().map(|block| block.block_id).collect();
|
||||||
|
|
||||||
self.editor
|
self.editor
|
||||||
|
@ -346,7 +346,7 @@ impl Session {
|
||||||
|
|
||||||
let message: JupyterMessage = execute_request.into();
|
let message: JupyterMessage = execute_request.into();
|
||||||
|
|
||||||
let mut blocks_to_remove: HashSet<BlockId> = HashSet::default();
|
let mut blocks_to_remove: HashSet<CustomBlockId> = HashSet::default();
|
||||||
|
|
||||||
let buffer = editor.read(cx).buffer().read(cx).snapshot(cx);
|
let buffer = editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||||
|
|
||||||
|
|
|
@ -258,7 +258,9 @@ impl Render for ProjectIndexDebugView {
|
||||||
list.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
|
list.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
|
||||||
list
|
list
|
||||||
},
|
},
|
||||||
|_, mut list, cx| list.paint(cx),
|
|_, mut list, cx| {
|
||||||
|
list.paint(cx);
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.size_full()
|
.size_full()
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue