feat: add panes and shared-buffer splits
This commit is contained in:
+75
-10
@@ -145,6 +145,7 @@ struct TrackedDocument {
|
||||
#[derive(Clone, Copy)]
|
||||
struct InvocationSource {
|
||||
buffer_id: usize,
|
||||
pane_id: usize,
|
||||
revision: u64,
|
||||
}
|
||||
|
||||
@@ -270,6 +271,11 @@ impl EditorSession {
|
||||
&self.editor
|
||||
}
|
||||
|
||||
/// Rendering-only mutable access; document synchronization stays in session dispatch.
|
||||
pub(crate) fn editor_mut(&mut self) -> &mut Editor {
|
||||
&mut self.editor
|
||||
}
|
||||
|
||||
pub(crate) fn set_diff_viewport(
|
||||
&mut self,
|
||||
comparison_id: u64,
|
||||
@@ -313,11 +319,14 @@ impl EditorSession {
|
||||
}
|
||||
|
||||
fn start_diff_request(&mut self) {
|
||||
if self.editor.diff().is_none() {
|
||||
if self.editor.workspace_diff().is_none() {
|
||||
self.diff_worker = None;
|
||||
return;
|
||||
}
|
||||
if self.editor.diff().is_some_and(|diff| !diff.loading)
|
||||
if self
|
||||
.editor
|
||||
.workspace_diff()
|
||||
.is_some_and(|diff| !diff.loading)
|
||||
&& let Some(worker) = &mut self.diff_worker
|
||||
&& let Some(id) = worker.pending.take()
|
||||
{
|
||||
@@ -328,13 +337,7 @@ impl EditorSession {
|
||||
return;
|
||||
};
|
||||
if self.diff_worker.is_none() {
|
||||
let root = self
|
||||
.editor
|
||||
.path()
|
||||
.and_then(Path::parent)
|
||||
.filter(|path| !path.as_os_str().is_empty())
|
||||
.unwrap_or_else(|| self.editor.project_root())
|
||||
.to_path_buf();
|
||||
let root = self.editor.diff_root();
|
||||
match DiffWorker::start(root) {
|
||||
Ok(worker) => self.diff_worker = Some(worker),
|
||||
Err(error) => {
|
||||
@@ -396,6 +399,7 @@ impl EditorSession {
|
||||
let context = self.editor.context();
|
||||
let source = InvocationSource {
|
||||
buffer_id: context.buffer_id,
|
||||
pane_id: self.editor.pane_id(),
|
||||
revision: context.revision,
|
||||
};
|
||||
self.requests.entry("core".into()).or_default().latest = Some((id, source));
|
||||
@@ -431,6 +435,7 @@ impl EditorSession {
|
||||
.map(|id| {
|
||||
let source = InvocationSource {
|
||||
buffer_id: context.buffer_id,
|
||||
pane_id: self.editor.pane_id(),
|
||||
revision: context.revision,
|
||||
};
|
||||
requests.pending.insert(id, source);
|
||||
@@ -484,6 +489,7 @@ impl EditorSession {
|
||||
if let Some(requests) = self.requests.get_mut("core")
|
||||
&& requests.latest.is_some_and(|(_, source)| {
|
||||
source.buffer_id != self.editor.buffer_id()
|
||||
|| source.pane_id != self.editor.pane_id()
|
||||
|| source.revision != self.editor.text_revision()
|
||||
|| self.lsp_source_cursor != Some(self.editor.cursor())
|
||||
|| self.editor.mode() != self.lsp_source_mode
|
||||
@@ -618,6 +624,7 @@ impl EditorSession {
|
||||
.is_some_and(|(id, latest)| {
|
||||
request_id == Some(id)
|
||||
&& latest.buffer_id == source.buffer_id
|
||||
&& latest.pane_id == self.editor.pane_id()
|
||||
&& latest.revision == source.revision
|
||||
})
|
||||
}
|
||||
@@ -674,6 +681,7 @@ impl EditorSession {
|
||||
}
|
||||
let source = InvocationSource {
|
||||
buffer_id: *buffer_id,
|
||||
pane_id: self.editor.pane_id(),
|
||||
revision: *revision,
|
||||
};
|
||||
if self.completion_is_current(update.request_id, source)
|
||||
@@ -720,6 +728,7 @@ impl EditorSession {
|
||||
} => {
|
||||
let source = InvocationSource {
|
||||
buffer_id: *buffer_id,
|
||||
pane_id: self.editor.pane_id(),
|
||||
revision: *revision,
|
||||
};
|
||||
if self.request_is_current(owner, update.request_id, source)
|
||||
@@ -738,6 +747,7 @@ impl EditorSession {
|
||||
} => {
|
||||
let source = InvocationSource {
|
||||
buffer_id: *buffer_id,
|
||||
pane_id: self.editor.pane_id(),
|
||||
revision: *revision,
|
||||
};
|
||||
if self.hover_is_current(owner, update.request_id, source) {
|
||||
@@ -786,6 +796,7 @@ impl EditorSession {
|
||||
} => {
|
||||
let source = InvocationSource {
|
||||
buffer_id,
|
||||
pane_id: self.editor.pane_id(),
|
||||
revision,
|
||||
};
|
||||
if self.request_is_current(owner, update.request_id, source) {
|
||||
@@ -805,6 +816,7 @@ impl EditorSession {
|
||||
} => {
|
||||
let source = InvocationSource {
|
||||
buffer_id,
|
||||
pane_id: self.editor.pane_id(),
|
||||
revision,
|
||||
};
|
||||
if self.hover_is_current(owner, update.request_id, source) {
|
||||
@@ -823,6 +835,7 @@ impl EditorSession {
|
||||
} => {
|
||||
let source = InvocationSource {
|
||||
buffer_id,
|
||||
pane_id: self.editor.pane_id(),
|
||||
revision,
|
||||
};
|
||||
if self.completion_is_current(update.request_id, source) {
|
||||
@@ -857,7 +870,10 @@ impl EditorSession {
|
||||
let Some(source) = source else {
|
||||
return Err("plugin response has an unknown invocation ID".into());
|
||||
};
|
||||
if !self.source_is_current(source) || self.editor.buffer_id() != source.buffer_id {
|
||||
if !self.source_is_current(source)
|
||||
|| self.editor.buffer_id() != source.buffer_id
|
||||
|| self.editor.pane_id() != source.pane_id
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
if effects
|
||||
@@ -948,6 +964,53 @@ mod tests {
|
||||
|
||||
static NEXT_FIXTURE: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[test]
|
||||
fn render_swaps_preserve_session_sources_and_pending_effects_do_not_cross_panes() {
|
||||
let fixture = Fixture::new();
|
||||
let path = fixture.file("buffer.txt", "source");
|
||||
let mut session = EditorSession::open(Some(path), vec![], None).unwrap();
|
||||
let source = InvocationSource {
|
||||
pane_id: session.editor.pane_id(),
|
||||
buffer_id: session.editor.buffer_id(),
|
||||
revision: session.editor.text_revision(),
|
||||
};
|
||||
session
|
||||
.requests
|
||||
.entry("fixture".into())
|
||||
.or_default()
|
||||
.pending
|
||||
.insert(1, source);
|
||||
session.requests.entry("fixture".into()).or_default().latest = Some((1, source));
|
||||
session.handle(Input::Char('|'));
|
||||
assert_eq!(session.editor.buffer_id(), source.buffer_id);
|
||||
assert_eq!(session.editor.cursor(), Cursor::default());
|
||||
assert!(!session.request_is_current("fixture", Some(1), source));
|
||||
assert!(
|
||||
!session
|
||||
.apply_response("fixture", 1, vec![Effect::Insert("wrong pane".into())])
|
||||
.unwrap()
|
||||
);
|
||||
session.requests.entry("core".into()).or_default().latest = Some((
|
||||
2,
|
||||
InvocationSource {
|
||||
pane_id: session.editor.pane_id(),
|
||||
..source
|
||||
},
|
||||
));
|
||||
session.lsp_source_cursor = Some(session.editor.cursor());
|
||||
session.lsp_source_mode = session.editor.mode();
|
||||
session.editor_mut().with_render_pane(1, |pane| {
|
||||
assert_eq!(pane.pane_id(), 1);
|
||||
assert_eq!(pane.lines(), ["source"]);
|
||||
});
|
||||
session.synchronize_documents();
|
||||
assert!(session.requests["core"].latest.is_some());
|
||||
assert_eq!(session.editor.pane_id(), 2);
|
||||
session.handle(Input::Alt('h'));
|
||||
assert!(session.requests["core"].latest.is_none());
|
||||
assert_eq!(session.editor.lines(), ["source"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_semantic_batches_are_atomic_and_malformed_updates_clear_hidden_overlays() {
|
||||
use crate::plugin::{SemanticToken, SemanticTokenKind};
|
||||
@@ -3412,6 +3475,7 @@ end
|
||||
session.editor.handle(Input::BackTab);
|
||||
let source = InvocationSource {
|
||||
buffer_id: session.editor.buffer_id(),
|
||||
pane_id: session.editor.pane_id(),
|
||||
revision: session.editor.text_revision(),
|
||||
};
|
||||
session
|
||||
@@ -3447,6 +3511,7 @@ end
|
||||
session.editor.scroll_diff(Focus::Before, false);
|
||||
let readonly = InvocationSource {
|
||||
buffer_id: session.editor.buffer_id(),
|
||||
pane_id: session.editor.pane_id(),
|
||||
revision: session.editor.text_revision(),
|
||||
};
|
||||
session
|
||||
|
||||
Reference in New Issue
Block a user