Apply CreateBranch and CreateDocument operations

This commit is contained in:
Antonio Scandurra 2023-07-20 11:40:39 +02:00
parent 30bac17749
commit 900deaab50
5 changed files with 146 additions and 60 deletions

3
Cargo.lock generated
View file

@ -1740,9 +1740,12 @@ dependencies = [
"anyhow", "anyhow",
"async-broadcast", "async-broadcast",
"collections", "collections",
"ctor",
"env_logger 0.9.3",
"futures 0.3.28", "futures 0.3.28",
"gpui", "gpui",
"lazy_static", "lazy_static",
"log",
"parking_lot 0.11.2", "parking_lot 0.11.2",
"rand 0.8.5", "rand 0.8.5",
"rope", "rope",

View file

@ -16,6 +16,7 @@ util = { path = "../util" }
anyhow.workspace = true anyhow.workspace = true
futures.workspace = true futures.workspace = true
lazy_static.workspace = true lazy_static.workspace = true
log.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
serde.workspace = true serde.workspace = true
serde_bare = "0.5" serde_bare = "0.5"
@ -23,6 +24,8 @@ smallvec.workspace = true
uuid = { version = "1.3", features = ["v4", "fast-rng", "serde"] } uuid = { version = "1.3", features = ["v4", "fast-rng", "serde"] }
[dev-dependencies] [dev-dependencies]
ctor.workspace = true
env_logger.workspace = true
gpui = { path = "../gpui", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] }
async-broadcast = "0.4" async-broadcast = "0.4"
rand.workspace = true rand.workspace = true

View file

@ -11,7 +11,6 @@ use collections::{btree_map, BTreeMap, Bound, HashMap, VecDeque};
use dense_id::DenseId; use dense_id::DenseId;
use futures::{channel::mpsc, future::BoxFuture, FutureExt, StreamExt}; use futures::{channel::mpsc, future::BoxFuture, FutureExt, StreamExt};
use messages::{MessageEnvelope, Operation, RequestEnvelope}; use messages::{MessageEnvelope, Operation, RequestEnvelope};
use operations::CreateBranch;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use rope::Rope; use rope::Rope;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -646,41 +645,18 @@ struct Branch {
impl Branch { impl Branch {
pub fn create_document(&self) -> Document { pub fn create_document(&self) -> Document {
self.update(|document_id, parent, revision| { self.update(|document_id, parent, revision| {
let mut cursor = revision.document_fragments.cursor::<OperationId>(); let operation = operations::CreateDocument {
let mut new_document_fragments = cursor.slice(&document_id, Bias::Right, &());
new_document_fragments.push(
DocumentFragment {
document_id,
location: DenseId::min(),
insertion_id: document_id,
insertion_subrange: 0..0,
tombstones: Default::default(),
undo_count: 0,
},
&(),
);
new_document_fragments.append(cursor.suffix(&()), &());
drop(cursor);
revision.document_fragments = new_document_fragments;
revision.document_metadata.insert(
document_id,
DocumentMetadata {
path: None,
last_change: document_id,
},
);
let operation = Operation::CreateDocument(operations::CreateDocument {
id: document_id, id: document_id,
branch_id: self.id,
parent, parent,
}); };
revision.apply_create_document(operation.clone());
let document = Document { let document = Document {
id: document_id, id: document_id,
branch: self.clone(), branch: self.clone(),
}; };
(operation, document) (Operation::CreateDocument(operation), document)
}) })
} }
@ -879,6 +855,7 @@ impl Document {
let edits = edits.into_iter(); let edits = edits.into_iter();
let mut edit_op = operations::Edit { let mut edit_op = operations::Edit {
id: operation_id, id: operation_id,
branch_id: self.branch.id,
parent: parent.clone(), parent: parent.clone(),
edits: SmallVec::with_capacity(edits.len()), edits: SmallVec::with_capacity(edits.len()),
}; };
@ -1247,7 +1224,7 @@ impl<'a> RopeBuilder<'a> {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug)]
pub struct RepoSnapshot { pub struct RepoSnapshot {
last_operation_id: OperationId, last_operation_id: OperationId,
branches: btree::Map<OperationId, BranchSnapshot>, branches: btree::Map<OperationId, BranchSnapshot>,
@ -1257,6 +1234,22 @@ pub struct RepoSnapshot {
deferred_operations: btree::Sequence<DeferredOperation>, deferred_operations: btree::Sequence<DeferredOperation>,
} }
impl Default for RepoSnapshot {
fn default() -> Self {
Self {
last_operation_id: Default::default(),
branches: Default::default(),
operations: Default::default(),
revisions: btree::Map::from_ordered_entries([(
RevisionId::default(),
Revision::default(),
)]),
max_operation_ids: Default::default(),
deferred_operations: Default::default(),
}
}
}
impl RepoSnapshot { impl RepoSnapshot {
fn new(replica_id: ReplicaId) -> Self { fn new(replica_id: ReplicaId) -> Self {
Self { Self {
@ -1268,21 +1261,17 @@ impl RepoSnapshot {
fn create_empty_branch(&mut self, name: impl Into<Arc<str>>) -> (Operation, OperationId) { fn create_empty_branch(&mut self, name: impl Into<Arc<str>>) -> (Operation, OperationId) {
let name = name.into(); let name = name.into();
let branch_id = self.last_operation_id.tick(); let branch_id = self.last_operation_id.tick();
self.branches.insert( let operation = operations::CreateBranch {
branch_id,
BranchSnapshot {
name: name.clone(),
head: smallvec![branch_id],
},
);
self.revisions
.insert(smallvec![branch_id], Default::default());
let operation = Operation::CreateBranch(CreateBranch {
id: branch_id, id: branch_id,
name, name,
parent: Default::default(), parent: Default::default(),
}); };
(operation, branch_id) operation
.clone()
.apply(self)
.expect("can always create empty branch");
(Operation::CreateBranch(operation), branch_id)
} }
fn operations_since(&self, version: &btree::Map<ReplicaId, OperationCount>) -> Vec<Operation> { fn operations_since(&self, version: &btree::Map<ReplicaId, OperationCount>) -> Vec<Operation> {
@ -1315,27 +1304,30 @@ impl RepoSnapshot {
} }
/// Apply the given operations and any deferred operations that are now applicable. /// Apply the given operations and any deferred operations that are now applicable.
fn apply_operations(&mut self, operations: impl Into<VecDeque<Operation>>) -> Result<()> { fn apply_operations(&mut self, operations: impl Into<VecDeque<Operation>>) {
let mut operations = operations.into(); let mut operations = operations.into();
while let Some(operation) = operations.pop_front() { while let Some(operation) = operations.pop_front() {
self.save_operation(&operation); self.save_operation(&operation);
if operation if operation
.revision() .parent()
.iter() .iter()
.all(|parent| self.operations.contains_key(&parent)) .all(|parent| self.operations.contains_key(&parent))
{ {
let operation_id = operation.id(); let operation_id = operation.id();
match operation { let result = match operation {
Operation::CreateDocument(op) => op.apply(self), Operation::CreateDocument(op) => op.apply(self),
Operation::Edit(op) => op.apply(self), Operation::Edit(op) => op.apply(self),
Operation::CreateBranch(op) => op.apply(self), Operation::CreateBranch(op) => op.apply(self),
}?; };
match result {
self.flush_deferred_operations(operation_id, &mut operations); Ok(_) => self.flush_deferred_operations(operation_id, &mut operations),
Err(error) => {
log::error!("error applying operation {:?}: {:?}", operation_id, error)
}
}
} else { } else {
for parent in operation.revision() { for parent in operation.parent() {
self.deferred_operations.insert_or_replace( self.deferred_operations.insert_or_replace(
DeferredOperation { DeferredOperation {
parent: *parent, parent: *parent,
@ -1346,7 +1338,6 @@ impl RepoSnapshot {
} }
} }
} }
Ok(())
} }
/// Remove any operations deferred on the given parent and add them to the /// Remove any operations deferred on the given parent and add them to the
@ -1447,12 +1438,52 @@ struct Revision {
hidden_text: Rope, hidden_text: Rope,
} }
impl Revision {
fn apply_create_document(&mut self, operation: operations::CreateDocument) {
let mut cursor = self.document_fragments.cursor::<OperationId>();
let mut new_document_fragments = cursor.slice(&operation.id, Bias::Right, &());
new_document_fragments.push(
DocumentFragment {
document_id: operation.id,
location: DenseId::min(),
insertion_id: operation.id,
insertion_subrange: 0..0,
tombstones: Default::default(),
undo_count: 0,
},
&(),
);
new_document_fragments.append(cursor.suffix(&()), &());
drop(cursor);
self.document_fragments = new_document_fragments;
self.document_metadata.insert(
operation.id,
DocumentMetadata {
path: None,
last_change: operation.id,
},
);
}
}
#[cfg(test)]
#[ctor::ctor]
fn init_logger() {
if std::env::var("RUST_LOG").is_ok() {
env_logger::init();
} else {
env_logger::builder()
.filter_level(log::LevelFilter::Error)
.init();
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use gpui::executor::{Background, Deterministic};
use super::*; use super::*;
use crate::test::TestNetwork; use crate::test::TestNetwork;
use gpui::executor::{Background, Deterministic};
#[gpui::test] #[gpui::test]
async fn test_repo(deterministic: Arc<Deterministic>) { async fn test_repo(deterministic: Arc<Deterministic>) {

View file

@ -142,7 +142,7 @@ impl Operation {
} }
} }
pub fn revision(&self) -> &RevisionId { pub fn parent(&self) -> &RevisionId {
match self { match self {
Operation::CreateDocument(op) => &op.parent, Operation::CreateDocument(op) => &op.parent,
Operation::Edit(op) => &op.parent, Operation::Edit(op) => &op.parent,

View file

@ -1,8 +1,12 @@
use crate::{AnchorRange, OperationId, RepoSnapshot, RevisionId}; use crate::{
use anyhow::Result; dense_id::DenseId, AnchorRange, BranchSnapshot, DocumentFragment, DocumentMetadata,
OperationId, RepoSnapshot, Revision, RevisionId,
};
use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::SmallVec; use smallvec::{smallvec, SmallVec};
use std::sync::Arc; use std::sync::Arc;
use sum_tree::Bias;
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateBranch { pub struct CreateBranch {
@ -13,31 +17,76 @@ pub struct CreateBranch {
impl CreateBranch { impl CreateBranch {
pub fn apply(self, repo: &mut RepoSnapshot) -> Result<()> { pub fn apply(self, repo: &mut RepoSnapshot) -> Result<()> {
todo!() let revision = repo
.revisions
.get(&self.parent)
.ok_or_else(|| anyhow!("parent revision not found"))?
.clone();
repo.branches.insert(
self.id,
BranchSnapshot {
name: self.name,
head: smallvec![self.id],
},
);
repo.revisions.insert(smallvec![self.id], revision);
Ok(())
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateDocument { pub struct CreateDocument {
pub id: OperationId, pub id: OperationId,
pub branch_id: OperationId,
pub parent: RevisionId, pub parent: RevisionId,
} }
impl CreateDocument { impl CreateDocument {
pub fn apply(self, repo: &mut RepoSnapshot) -> Result<()> { pub fn apply(self, repo: &mut RepoSnapshot) -> Result<()> {
todo!() let branch_id = self.branch_id;
let branch = repo
.branches
.get(&self.branch_id)
.ok_or_else(|| anyhow!("branch {:?} not found", self.branch_id))?;
let mut revision = repo
.revisions
.get(&branch.head)
.ok_or_else(|| {
anyhow!(
"revision {:?} not found in branch {:?}",
branch.head,
self.branch_id
)
})?
.clone();
let new_head = if self.parent == branch.head {
smallvec![self.id]
} else {
let mut head = branch.head.clone();
head.push(self.id);
head
};
revision.apply_create_document(self);
repo.branches
.update(&branch_id, |branch| branch.head = new_head.clone());
repo.revisions.insert(new_head, revision);
Ok(())
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Edit { pub struct Edit {
pub id: OperationId, pub id: OperationId,
pub branch_id: OperationId,
pub parent: RevisionId, pub parent: RevisionId,
pub edits: SmallVec<[(AnchorRange, Arc<str>); 2]>, pub edits: SmallVec<[(AnchorRange, Arc<str>); 2]>,
} }
impl Edit { impl Edit {
pub fn apply(self, repo: &mut RepoSnapshot) -> Result<()> { pub fn apply(self, repo: &mut RepoSnapshot) -> Result<()> {
todo!() Err(anyhow!("not implemented"))
} }
} }