mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-22 23:34:49 +01:00
fix: better api with TextChange, ugly fixes
it's pretty ugly but it kinda works? really need to do this better
This commit is contained in:
parent
8267e1b890
commit
ca29ee3e0f
3 changed files with 96 additions and 39 deletions
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use tokio::sync::{watch, mpsc};
|
use tokio::sync::{watch, mpsc, Mutex, RwLock, TryLockError};
|
||||||
use tonic::async_trait;
|
use tonic::async_trait;
|
||||||
|
|
||||||
use crate::errors::IgnorableError;
|
use crate::errors::IgnorableError;
|
||||||
|
@ -32,6 +32,7 @@ use super::TextChange;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BufferController {
|
pub struct BufferController {
|
||||||
content: watch::Receiver<String>,
|
content: watch::Receiver<String>,
|
||||||
|
seen: Arc<RwLock<String>>,
|
||||||
operations: mpsc::UnboundedSender<TextChange>,
|
operations: mpsc::UnboundedSender<TextChange>,
|
||||||
_stop: Arc<StopOnDrop>, // just exist
|
_stop: Arc<StopOnDrop>, // just exist
|
||||||
}
|
}
|
||||||
|
@ -42,7 +43,11 @@ impl BufferController {
|
||||||
operations: mpsc::UnboundedSender<TextChange>,
|
operations: mpsc::UnboundedSender<TextChange>,
|
||||||
stop: mpsc::UnboundedSender<()>,
|
stop: mpsc::UnboundedSender<()>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
BufferController { content, operations, _stop: Arc::new(StopOnDrop(stop)) }
|
BufferController {
|
||||||
|
content, operations,
|
||||||
|
_stop: Arc::new(StopOnDrop(stop)),
|
||||||
|
seen: Arc::new(RwLock::new("".into())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,19 +61,56 @@ impl Drop for StopOnDrop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Controller<String> for BufferController {
|
impl Controller<TextChange> for BufferController {
|
||||||
type Input = TextChange;
|
type Input = TextChange;
|
||||||
|
|
||||||
async fn poll(&self) -> Result<(), Error> {
|
async fn poll(&self) -> Result<(), Error> {
|
||||||
Ok(self.content.clone().changed().await?)
|
let mut poller = self.content.clone();
|
||||||
|
loop {
|
||||||
|
poller.changed().await?;
|
||||||
|
let seen = self.seen.read().await.clone();
|
||||||
|
if *poller.borrow() != seen {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_recv(&self) -> Result<Option<String>, Error> {
|
fn try_recv(&self) -> Result<Option<TextChange>, Error> {
|
||||||
Ok(Some(self.content.borrow().clone()))
|
let cur = match self.seen.try_read() {
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("try_recv invoked while being mutated: {}", e);
|
||||||
|
return Ok(None);
|
||||||
|
},
|
||||||
|
Ok(x) => x.clone(),
|
||||||
|
};
|
||||||
|
if *self.content.borrow() != cur {
|
||||||
|
match self.seen.try_write() {
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("try_recv mutating while being mutated: {}", e);
|
||||||
|
return Ok(None);
|
||||||
|
},
|
||||||
|
Ok(mut w) => {
|
||||||
|
*w = self.content.borrow().clone();
|
||||||
|
// TODO it's not the whole buffer that changed
|
||||||
|
return Ok(Some(TextChange {
|
||||||
|
span: 0..cur.len(),
|
||||||
|
content: self.content.borrow().clone(),
|
||||||
|
after: "".to_string(),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn recv(&self) -> Result<String, Error> {
|
}
|
||||||
Ok(self.content.borrow().clone())
|
}
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn recv(&self) -> Result<TextChange, Error> {
|
||||||
|
self.poll().await?;
|
||||||
|
match self.try_recv()? {
|
||||||
|
Some(x) => Ok(x),
|
||||||
|
None => Err(crate::Error::Filler { message: "wtfff".into() }),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// enqueue an opseq for processing
|
/// enqueue an opseq for processing
|
||||||
|
|
|
@ -8,6 +8,7 @@ use tonic::{async_trait, Streaming};
|
||||||
use woot::crdt::{Op, CRDT, TextEditor};
|
use woot::crdt::{Op, CRDT, TextEditor};
|
||||||
use woot::woot::Woot;
|
use woot::woot::Woot;
|
||||||
|
|
||||||
|
use crate::errors::IgnorableError;
|
||||||
use crate::proto::{OperationRequest, RawOp};
|
use crate::proto::{OperationRequest, RawOp};
|
||||||
use crate::proto::buffer_client::BufferClient;
|
use crate::proto::buffer_client::BufferClient;
|
||||||
use crate::api::controller::ControllerWorker;
|
use crate::api::controller::ControllerWorker;
|
||||||
|
@ -29,8 +30,8 @@ pub(crate) struct BufferControllerWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferControllerWorker {
|
impl BufferControllerWorker {
|
||||||
pub fn new(uid: String, buffer: &str, path: &str) -> Self {
|
pub fn new(uid: String, path: &str) -> Self {
|
||||||
let (txt_tx, txt_rx) = watch::channel(buffer.to_string());
|
let (txt_tx, txt_rx) = watch::channel("".to_string());
|
||||||
let (op_tx, op_rx) = mpsc::unbounded_channel();
|
let (op_tx, op_rx) = mpsc::unbounded_channel();
|
||||||
let (end_tx, end_rx) = mpsc::unbounded_channel();
|
let (end_tx, end_rx) = mpsc::unbounded_channel();
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
|
@ -42,7 +43,7 @@ impl BufferControllerWorker {
|
||||||
operations: op_rx,
|
operations: op_rx,
|
||||||
receiver: txt_rx,
|
receiver: txt_rx,
|
||||||
sender: op_tx,
|
sender: op_tx,
|
||||||
buffer: Woot::new(site_id, buffer), // TODO initialize with buffer!
|
buffer: Woot::new(site_id, ""), // TODO initialize with buffer!
|
||||||
path: path.to_string(),
|
path: path.to_string(),
|
||||||
stop: end_rx,
|
stop: end_rx,
|
||||||
stop_control: end_tx,
|
stop_control: end_tx,
|
||||||
|
@ -64,7 +65,7 @@ impl BufferControllerWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ControllerWorker<String> for BufferControllerWorker {
|
impl ControllerWorker<TextChange> for BufferControllerWorker {
|
||||||
type Controller = BufferController;
|
type Controller = BufferController;
|
||||||
type Tx = BufferClient<Channel>;
|
type Tx = BufferClient<Channel>;
|
||||||
type Rx = Streaming<RawOp>;
|
type Rx = Streaming<RawOp>;
|
||||||
|
@ -90,7 +91,9 @@ impl ControllerWorker<String> for BufferControllerWorker {
|
||||||
res = self.operations.recv() => match res {
|
res = self.operations.recv() => match res {
|
||||||
None => break,
|
None => break,
|
||||||
Some(change) => {
|
Some(change) => {
|
||||||
let span = &self.buffer.view()[change.span.clone()];
|
match self.buffer.view().get(change.span.clone()) {
|
||||||
|
None => tracing::error!("received illegal span from client"),
|
||||||
|
Some(span) => {
|
||||||
let diff = TextDiff::from_chars(span, &change.content);
|
let diff = TextDiff::from_chars(span, &change.content);
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
@ -98,11 +101,19 @@ impl ControllerWorker<String> for BufferControllerWorker {
|
||||||
for diff in diff.iter_all_changes() {
|
for diff in diff.iter_all_changes() {
|
||||||
match diff.tag() {
|
match diff.tag() {
|
||||||
ChangeTag::Equal => i += 1,
|
ChangeTag::Equal => i += 1,
|
||||||
ChangeTag::Delete => ops.push(self.buffer.delete(change.span.start + i).unwrap()),
|
ChangeTag::Delete => match self.buffer.delete(change.span.start + i) {
|
||||||
|
Ok(op) => ops.push(op),
|
||||||
|
Err(e) => tracing::error!("could not apply deletion: {}", e),
|
||||||
|
},
|
||||||
ChangeTag::Insert => {
|
ChangeTag::Insert => {
|
||||||
for c in diff.value().chars() {
|
for c in diff.value().chars() {
|
||||||
ops.push(self.buffer.insert(change.span.start + i, c).unwrap());
|
match self.buffer.insert(change.span.start + i, c) {
|
||||||
|
Ok(op) => {
|
||||||
|
ops.push(op);
|
||||||
i += 1;
|
i += 1;
|
||||||
|
},
|
||||||
|
Err(e) => tracing::error!("could not apply insertion: {}", e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -110,10 +121,14 @@ impl ControllerWorker<String> for BufferControllerWorker {
|
||||||
|
|
||||||
for op in ops {
|
for op in ops {
|
||||||
match self.send_op(&mut tx, &op).await {
|
match self.send_op(&mut tx, &op).await {
|
||||||
Ok(()) => self.buffer.merge(op),
|
|
||||||
Err(e) => tracing::error!("server refused to broadcast {}: {}", op, e),
|
Err(e) => tracing::error!("server refused to broadcast {}: {}", op, e),
|
||||||
|
Ok(()) => {
|
||||||
|
self.content.send(self.buffer.view()).unwrap_or_warn("could not send buffer update");
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -121,10 +136,12 @@ impl ControllerWorker<String> for BufferControllerWorker {
|
||||||
res = rx.message() => match res {
|
res = rx.message() => match res {
|
||||||
Err(_e) => break,
|
Err(_e) => break,
|
||||||
Ok(None) => break,
|
Ok(None) => break,
|
||||||
Ok(Some(change)) => {
|
Ok(Some(change)) => match serde_json::from_str::<Op>(&change.opseq) {
|
||||||
let op : Op = serde_json::from_str(&change.opseq).unwrap();
|
Ok(op) => {
|
||||||
self.buffer.merge(op);
|
self.buffer.merge(op);
|
||||||
self.content.send(self.buffer.view()).unwrap();
|
self.content.send(self.buffer.view()).unwrap_or_warn("could not send buffer update");
|
||||||
|
},
|
||||||
|
Err(e) => tracing::error!("could not deserialize operation from server: {}", e),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,11 +132,9 @@ impl Client {
|
||||||
path: path.to_string(), user: self.id.clone(), content: None
|
path: path.to_string(), user: self.id.clone(), content: None
|
||||||
};
|
};
|
||||||
|
|
||||||
let content = client.sync(req.clone()).await?.into_inner().content;
|
|
||||||
|
|
||||||
let stream = client.attach(req).await?.into_inner();
|
let stream = client.attach(req).await?.into_inner();
|
||||||
|
|
||||||
let controller = BufferControllerWorker::new(self.id.clone(), &content, path);
|
let controller = BufferControllerWorker::new(self.id.clone(), path);
|
||||||
let handler = Arc::new(controller.subscribe());
|
let handler = Arc::new(controller.subscribe());
|
||||||
|
|
||||||
let _path = path.to_string();
|
let _path = path.to_string();
|
||||||
|
|
Loading…
Reference in a new issue