Cargo.lock 🔗
@@ -8664,6 +8664,7 @@ dependencies = [
"image",
"language",
"log",
+ "multi_buffer",
"project",
"runtimelib",
"schemars",
Kyle Kelley and Antonio created
Allows deleting the outputs directly within the editor. This also fixes
the overlap logic to make sure that the ends and the starts are
compared.
https://github.com/zed-industries/zed/assets/836375/84f5f582-95f3-4c6a-a3c9-54da6009e34d
Release Notes:
- N/A
---------
Co-authored-by: Antonio <antonio@zed.dev>
Cargo.lock | 1
crates/multi_buffer/src/anchor.rs | 6 --
crates/repl/Cargo.toml | 1
crates/repl/src/session.rs | 73 +++++++++++++++++++++++++++++++-
4 files changed, 72 insertions(+), 9 deletions(-)
@@ -8664,6 +8664,7 @@ dependencies = [
"image",
"language",
"log",
+ "multi_buffer",
"project",
"runtimelib",
"schemars",
@@ -131,11 +131,7 @@ impl AnchorRangeExt for Range<Anchor> {
}
fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
- let start_cmp = self.start.cmp(&other.start, buffer);
- let end_cmp = self.end.cmp(&other.end, buffer);
-
- (start_cmp == Ordering::Less || start_cmp == Ordering::Equal)
- && (end_cmp == Ordering::Greater || end_cmp == Ordering::Equal)
+ self.end.cmp(&other.start, buffer).is_ge() && self.start.cmp(&other.end, buffer).is_le()
}
fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize> {
@@ -24,6 +24,7 @@ futures.workspace = true
image.workspace = true
language.workspace = true
log.workspace = true
+multi_buffer.workspace = true
project.workspace = true
runtimelib.workspace = true
schemars.workspace = true
@@ -7,10 +7,13 @@ use editor::{
display_map::{
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock,
},
- Anchor, AnchorRangeExt as _, Editor,
+ Anchor, AnchorRangeExt as _, Editor, MultiBuffer, ToPoint,
};
use futures::{FutureExt as _, StreamExt as _};
-use gpui::{div, prelude::*, EventEmitter, Render, Task, View, ViewContext, WeakView};
+use gpui::{
+ div, prelude::*, EventEmitter, Model, Render, Subscription, Task, View, ViewContext, WeakView,
+};
+use language::Point;
use project::Fs;
use runtimelib::{
ExecuteRequest, InterruptRequest, JupyterMessage, JupyterMessageContent, KernelInfoRequest,
@@ -27,11 +30,13 @@ pub struct Session {
blocks: HashMap<String, EditorBlock>,
pub messaging_task: Task<()>,
pub kernel_specification: KernelSpecification,
+ _buffer_subscription: Subscription,
}
struct EditorBlock {
editor: WeakView<Editor>,
code_range: Range<Anchor>,
+ invalidation_anchor: Anchor,
block_id: BlockId,
execution_view: View<ExecutionView>,
}
@@ -45,7 +50,25 @@ impl EditorBlock {
) -> anyhow::Result<Self> {
let execution_view = cx.new_view(|cx| ExecutionView::new(status, cx));
- let block_id = editor.update(cx, |editor, cx| {
+ let (block_id, invalidation_anchor) = editor.update(cx, |editor, cx| {
+ let buffer = editor.buffer().clone();
+ let buffer_snapshot = buffer.read(cx).snapshot(cx);
+ let end_point = code_range.end.to_point(&buffer_snapshot);
+ let next_row_start = end_point + Point::new(1, 0);
+ if next_row_start > buffer_snapshot.max_point() {
+ buffer.update(cx, |buffer, cx| {
+ buffer.edit(
+ [(
+ buffer_snapshot.max_point()..buffer_snapshot.max_point(),
+ "\n",
+ )],
+ None,
+ cx,
+ )
+ });
+ }
+
+ let invalidation_anchor = buffer.read(cx).read(cx).anchor_before(next_row_start);
let block = BlockProperties {
position: code_range.end,
height: execution_view.num_lines(cx).saturating_add(1),
@@ -54,12 +77,14 @@ impl EditorBlock {
disposition: BlockDisposition::Below,
};
- editor.insert_blocks([block], None, cx)[0]
+ let block_id = editor.insert_blocks([block], None, cx)[0];
+ (block_id, invalidation_anchor)
})?;
anyhow::Ok(Self {
editor,
code_range,
+ invalidation_anchor,
block_id,
execution_view,
})
@@ -179,15 +204,55 @@ impl Session {
})
.shared();
+ let subscription = match editor.upgrade() {
+ Some(editor) => {
+ let buffer = editor.read(cx).buffer().clone();
+ cx.subscribe(&buffer, Self::on_buffer_event)
+ }
+ None => Subscription::new(|| {}),
+ };
+
return Self {
editor,
kernel: Kernel::StartingKernel(pending_kernel),
messaging_task: Task::ready(()),
blocks: HashMap::default(),
kernel_specification,
+ _buffer_subscription: subscription,
};
}
+ fn on_buffer_event(
+ &mut self,
+ buffer: Model<MultiBuffer>,
+ event: &multi_buffer::Event,
+ cx: &mut ViewContext<Self>,
+ ) {
+ if let multi_buffer::Event::Edited { .. } = event {
+ let snapshot = buffer.read(cx).snapshot(cx);
+
+ let mut blocks_to_remove: HashSet<BlockId> = HashSet::default();
+
+ self.blocks.retain(|_id, block| {
+ if block.invalidation_anchor.is_valid(&snapshot) {
+ true
+ } else {
+ blocks_to_remove.insert(block.block_id);
+ false
+ }
+ });
+
+ if !blocks_to_remove.is_empty() {
+ self.editor
+ .update(cx, |editor, cx| {
+ editor.remove_blocks(blocks_to_remove, None, cx);
+ })
+ .ok();
+ cx.notify();
+ }
+ }
+ }
+
fn send(&mut self, message: JupyterMessage, _cx: &mut ViewContext<Self>) -> anyhow::Result<()> {
match &mut self.kernel {
Kernel::RunningKernel(kernel) => {