Skip to content
This repository was archived by the owner on May 14, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
678 changes: 316 additions & 362 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
members = [
"memo_core",
"memo_js",
"xray_shared",
"xray_rpc",
"xray_core",
"xray_server",
"xray_cli",
Expand Down
48 changes: 48 additions & 0 deletions memo_core/src/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,54 @@ impl Epoch {
Ok(self.metadata(file_id)?.file_type)
}

pub fn iter_at_point(&self, file_id: FileId, point: Point) -> Result<buffer::Iter, Error> {
if let Some(TextFile::Buffered(buffer)) = self.text_files.get(&file_id) {
Ok(buffer.iter_at_point(point))
} else {
Err(Error::InvalidFileId("file has not been opened".into()))
}
}

pub fn max_point(&self, file_id: FileId) -> Result<Point, Error> {
if let Some(TextFile::Buffered(buffer)) = self.text_files.get(&file_id) {
Ok(buffer.max_point())
} else {
Err(Error::InvalidFileId("file has not been opened".into()))
}
}

pub fn longest_row(&self, file_id: FileId) -> Result<u32, Error> {
if let Some(TextFile::Buffered(buffer)) = self.text_files.get(&file_id) {
Ok(buffer.longest_row())
} else {
Err(Error::InvalidFileId("file has not been opened".into()))
}
}

pub fn line(&self, file_id: FileId, row: u32) -> Result<Vec<u16>, Error> {
if let Some(TextFile::Buffered(buffer)) = self.text_files.get(&file_id) {
buffer.line(row)
} else {
Err(Error::InvalidFileId("file has not been opened".into()))
}
}

pub fn len(&self, file_id: FileId) -> Result<usize, Error> {
if let Some(TextFile::Buffered(buffer)) = self.text_files.get(&file_id) {
Ok(buffer.len())
} else {
Err(Error::InvalidFileId("file has not been opened".into()))
}
}

pub fn len_for_row(&self, file_id: FileId, row: u32) -> Result<u32, Error> {
if let Some(TextFile::Buffered(buffer)) = self.text_files.get(&file_id) {
buffer.len_for_row(row)
} else {
Err(Error::InvalidFileId("file has not been opened".into()))
}
}

fn metadata(&self, file_id: FileId) -> Result<Metadata, Error> {
if file_id == ROOT_FILE_ID {
Ok(Metadata {
Expand Down
4 changes: 2 additions & 2 deletions memo_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ pub mod time;
mod work_tree;

pub use crate::buffer::{Buffer, Change, Point};
pub use crate::epoch::{Cursor, DirEntry, Epoch, FileStatus, FileType, ROOT_FILE_ID};
pub use crate::epoch::{Cursor, DirEntry, Epoch, FileStatus, FileType, ROOT_FILE_ID, Id as EpochId};
pub use crate::work_tree::{
BufferId, BufferSelectionRanges, ChangeObserver, GitProvider, LocalSelectionSetId, Operation,
OperationEnvelope, WorkTree,
OperationEnvelope, WorkTree, Version
};
use std::borrow::Cow;
use std::fmt;
Expand Down
120 changes: 96 additions & 24 deletions memo_core/src/work_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use std::path::{Path, PathBuf};
use std::rc::Rc;

pub trait GitProvider {
fn base_entries(&self, oid: Oid) -> Box<Stream<Item = DirEntry, Error = io::Error>>;
fn base_text(&self, oid: Oid, path: &Path) -> Box<Future<Item = String, Error = io::Error>>;
fn base_entries(&self, oid: Oid) -> Box<dyn Stream<Item = DirEntry, Error = io::Error>>;
fn base_text(&self, oid: Oid, path: &Path)
-> Box<dyn Future<Item = String, Error = io::Error>>;
}

pub trait ChangeObserver {
Expand All @@ -32,8 +33,8 @@ pub struct WorkTree {
next_local_selection_set_id: Rc<RefCell<LocalSelectionSetId>>,
deferred_ops: Rc<RefCell<HashMap<epoch::Id, Vec<epoch::Operation>>>>,
lamport_clock: Rc<RefCell<time::Lamport>>,
git: Rc<GitProvider>,
observer: Option<Rc<ChangeObserver>>,
git: Rc<dyn GitProvider>,
observer: Option<Rc<dyn ChangeObserver>>,
}

#[derive(Serialize, Deserialize)]
Expand All @@ -42,6 +43,7 @@ pub struct Version {
epoch_version: time::Global,
}

#[derive(Serialize, Deserialize)]
pub struct OperationEnvelope {
pub epoch_head: Option<Oid>,
pub operation: Operation,
Expand Down Expand Up @@ -77,7 +79,7 @@ enum MaybeDone<F: Future> {
}

struct BaseTextRequest {
future: MaybeDone<Box<Future<Item = String, Error = io::Error>>>,
future: MaybeDone<Box<dyn Future<Item = String, Error = io::Error>>>,
path: PathBuf,
}

Expand All @@ -91,21 +93,21 @@ struct SwitchEpoch {
Rc<RefCell<HashMap<BufferId, HashMap<LocalSelectionSetId, buffer::SelectionSetId>>>>,
deferred_ops: Rc<RefCell<HashMap<epoch::Id, Vec<epoch::Operation>>>>,
lamport_clock: Rc<RefCell<time::Lamport>>,
git: Rc<GitProvider>,
observer: Option<Rc<ChangeObserver>>,
git: Rc<dyn GitProvider>,
observer: Option<Rc<dyn ChangeObserver>>,
}

impl WorkTree {
pub fn new<I>(
replica_id: ReplicaId,
base: Option<Oid>,
ops: I,
git: Rc<GitProvider>,
observer: Option<Rc<ChangeObserver>>,
git: Rc<dyn GitProvider>,
observer: Option<Rc<dyn ChangeObserver>>,
) -> Result<
(
WorkTree,
Box<Stream<Item = OperationEnvelope, Error = Error>>,
Box<dyn Stream<Item = OperationEnvelope, Error = Error>>,
),
Error,
>
Expand All @@ -126,9 +128,10 @@ impl WorkTree {
};

let ops = if ops.peek().is_none() {
Box::new(tree.reset(base)) as Box<Stream<Item = OperationEnvelope, Error = Error>>
Box::new(tree.reset(base)) as Box<dyn Stream<Item = OperationEnvelope, Error = Error>>
} else {
Box::new(tree.apply_ops(ops)?) as Box<Stream<Item = OperationEnvelope, Error = Error>>
Box::new(tree.apply_ops(ops)?)
as Box<dyn Stream<Item = OperationEnvelope, Error = Error>>
};

Ok((tree, ops))
Expand Down Expand Up @@ -225,7 +228,7 @@ impl WorkTree {
epoch.id, epoch.head, fixup_ops,
)));
Ok(epoch_streams.into_iter().fold(
fixup_ops_stream as Box<Stream<Item = OperationEnvelope, Error = Error>>,
fixup_ops_stream as Box<dyn Stream<Item = OperationEnvelope, Error = Error>>,
|acc, stream| Box::new(acc.chain(stream)),
))
} else {
Expand All @@ -237,7 +240,7 @@ impl WorkTree {
&mut self,
new_epoch_id: epoch::Id,
new_head: Option<Oid>,
) -> Box<Stream<Item = OperationEnvelope, Error = Error>> {
) -> Box<dyn Stream<Item = OperationEnvelope, Error = Error>> {
if self
.epoch
.as_ref()
Expand Down Expand Up @@ -269,7 +272,7 @@ impl WorkTree {
)))
})
.flatten(),
) as Box<Stream<Item = OperationEnvelope, Error = Error>>
) as Box<dyn Stream<Item = OperationEnvelope, Error = Error>>
} else {
Box::new(stream::empty())
};
Expand Down Expand Up @@ -437,7 +440,7 @@ impl WorkTree {
self.cur_epoch().file_id(path).is_ok()
}

pub fn open_text_file<P>(&self, path: P) -> Box<Future<Item = BufferId, Error = Error>>
pub fn open_text_file<P>(&self, path: P) -> Box<dyn Future<Item = BufferId, Error = Error>>
where
P: Into<PathBuf>,
{
Expand All @@ -454,11 +457,11 @@ impl WorkTree {
fn open_text_file_internal(
path: PathBuf,
epoch: Rc<RefCell<Epoch>>,
git: Rc<GitProvider>,
git: Rc<dyn GitProvider>,
buffers: Rc<RefCell<HashMap<BufferId, FileId>>>,
next_buffer_id: Rc<RefCell<BufferId>>,
lamport_clock: Rc<RefCell<time::Lamport>>,
) -> Box<Future<Item = BufferId, Error = Error>> {
) -> Box<dyn Future<Item = BufferId, Error = Error>> {
if let Some(buffer_id) = Self::existing_buffer(&epoch, &buffers, &path) {
Box::new(future::ok(buffer_id))
} else {
Expand Down Expand Up @@ -517,8 +520,8 @@ impl WorkTree {
fn base_text(
path: &Path,
epoch: &RefCell<Epoch>,
git: &GitProvider,
) -> Box<Future<Item = (FileId, String), Error = Error>> {
git: &dyn GitProvider,
) -> Box<dyn Future<Item = (FileId, String), Error = Error>> {
let epoch = epoch.borrow();
match epoch.file_id(&path) {
Ok(file_id) => {
Expand Down Expand Up @@ -743,6 +746,36 @@ impl WorkTree {
self.cur_epoch().buffer_deferred_ops_len(file_id)
}

pub fn max_point(&self, buffer_id: BufferId) -> Result<Point, Error> {
let file_id = self.buffer_file_id(buffer_id)?;
self.cur_epoch().max_point(file_id)
}

pub fn longest_row(&self, buffer_id: BufferId) -> Result<u32, Error> {
let file_id = self.buffer_file_id(buffer_id)?;
self.cur_epoch().longest_row(file_id)
}

pub fn line(&self, buffer_id: BufferId, row: u32) -> Result<Vec<u16>, Error> {
let file_id = self.buffer_file_id(buffer_id)?;
self.cur_epoch().line(file_id, row)
}

pub fn iter_at_point(&self, buffer_id: BufferId, point: Point) -> Result<buffer::Iter, Error> {
let file_id = self.buffer_file_id(buffer_id)?;
self.cur_epoch().iter_at_point(file_id, point)
}

pub fn len(&self, buffer_id: BufferId) -> Result<usize, Error> {
let file_id = self.buffer_file_id(buffer_id)?;
self.cur_epoch().len(file_id)
}

pub fn len_for_row(&self, buffer_id: BufferId, row: u32) -> Result<u32, Error> {
let file_id = self.buffer_file_id(buffer_id)?;
self.cur_epoch().len_for_row(file_id, row)
}

fn cur_epoch(&self) -> Ref<Epoch> {
self.epoch.as_ref().unwrap().borrow()
}
Expand Down Expand Up @@ -959,8 +992,8 @@ impl SwitchEpoch {
>,
deferred_ops: Rc<RefCell<HashMap<epoch::Id, Vec<epoch::Operation>>>>,
lamport_clock: Rc<RefCell<time::Lamport>>,
git: Rc<GitProvider>,
observer: Option<Rc<ChangeObserver>>,
git: Rc<dyn GitProvider>,
observer: Option<Rc<dyn ChangeObserver>>,
) -> Self {
let last_seen = cur_epoch.borrow().id;
Self {
Expand Down Expand Up @@ -1193,6 +1226,45 @@ impl<F: Future> MaybeDone<F> {
}
}

struct OperationVisitor;

impl<'de> serde::de::Deserialize<'de> for Operation {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
Ok(
Operation::deserialize(&deserializer.deserialize_bytes(OperationVisitor)?)
.unwrap()
.unwrap(),
)
}
}

impl serde::ser::Serialize for Operation {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_bytes(&self.serialize())
}
}

impl<'de> serde::de::Visitor<'de> for OperationVisitor {
type Value = Vec<u8>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "Dunno")
}

fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v.to_vec())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -1917,7 +1989,7 @@ mod tests {
}

impl GitProvider for TestGitProvider {
fn base_entries(&self, oid: Oid) -> Box<Stream<Item = DirEntry, Error = io::Error>> {
fn base_entries(&self, oid: Oid) -> Box<dyn Stream<Item = DirEntry, Error = io::Error>> {
match self.commits.borrow().get(&oid) {
Some(tree) => Box::new(stream::iter_ok(tree.dir_entries().into_iter())),
None => Box::new(stream::once(Err(io::Error::new(
Expand All @@ -1931,7 +2003,7 @@ mod tests {
&self,
oid: Oid,
path: &Path,
) -> Box<Future<Item = String, Error = io::Error>> {
) -> Box<dyn Future<Item = String, Error = io::Error>> {
use futures::IntoFuture;

Box::new(
Expand Down
4 changes: 2 additions & 2 deletions memo_js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ impl memo::GitProvider for GitProviderWrapper {
fn base_entries(
&self,
oid: memo::Oid,
) -> Box<Stream<Item = memo::DirEntry, Error = io::Error>> {
) -> Box<dyn Stream<Item = memo::DirEntry, Error = io::Error>> {
let iterator = GitProviderWrapper::base_entries(self, &hex::encode(oid));
Box::new(
AsyncIteratorToStream::new(iterator)
Expand All @@ -549,7 +549,7 @@ impl memo::GitProvider for GitProviderWrapper {
&self,
oid: memo::Oid,
path: &Path,
) -> Box<Future<Item = String, Error = io::Error>> {
) -> Box<dyn Future<Item = String, Error = io::Error>> {
Box::new(
JsFuture::from(GitProviderWrapper::base_text(
self,
Expand Down
2 changes: 1 addition & 1 deletion memo_js/test/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ suite("WorkTree", () => {
const tree2BufferChanges: Change[] = [];
tree2BufferC.onChange(c => tree2BufferChanges.push(...c.textChanges));
assert.deepStrictEqual(await collectOps(tree2.applyOps(ops1)), []);
assert.strictEqual(tree1BufferC.getText(), "oid0-base-text");
assert.strictEqual(tree2BufferC.getText(), "oid0-base-text");
assert.deepStrictEqual(tree1BufferChanges, []);
assert.deepStrictEqual(tree2BufferChanges, [
{ start: point(0, 4), end: point(0, 5), text: "-" },
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nightly-2019-01-26
nightly-2019-08-20
1 change: 1 addition & 0 deletions xray_browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "xray_browser",
"license": "MIT",
"dependencies": {
"uuid": "^3.3.2",
"xray_wasm": "../xray_wasm",
"xray_ui": "../xray_ui"
},
Expand Down
21 changes: 20 additions & 1 deletion xray_browser/src/ui.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import { React, ReactDOM, App, buildViewRegistry } from "xray_ui";
import * as uuid from "uuid/v4";
import XrayClient from "./client";
const $ = React.createElement;

const UUID_KEY = `xray-replica-id`
const getReplicaId = () => {
const storedId = localStorage.getItem(UUID_KEY)
if (storedId) {
return JSON.parse(storedId)
}

const id = new Array();
uuid(null, id)
localStorage.setItem(UUID_KEY, JSON.stringify(id))

return id
}

const client = new XrayClient(new Worker("worker.js"));
const websocketURL = new URL("/ws", window.location.href);
websocketURL.protocol = "ws";
client.sendMessage({ type: "ConnectToWebsocket", url: websocketURL.href });
client.sendMessage({
type: "ConnectToWebsocket",
url: websocketURL.href,
replica_id: getReplicaId()
});

const viewRegistry = buildViewRegistry(client);

Expand Down
Loading