Full Code of atom-archive/xray for AI

master cb6c5809f18c cached
141 files
1.1 MB
268.5k tokens
2280 symbols
1 requests
Download .txt
Showing preview only (1,195K chars total). Download the full file or copy to clipboard to get everything.
Repository: atom-archive/xray
Branch: master
Commit: cb6c5809f18c
Files: 141
Total size: 1.1 MB

Directory structure:
gitextract_hfrilqj1/

├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── Cargo.toml
├── LICENSE
├── README.md
├── docs/
│   ├── architecture/
│   │   ├── 001_client_server_protocol.md
│   │   ├── 002_shared_workspaces.md
│   │   └── 003_memo_epochs.md
│   └── updates/
│       ├── 2018_03_05.md
│       ├── 2018_03_12.md
│       ├── 2018_03_19.md
│       ├── 2018_03_26.md
│       ├── 2018_04_02.md
│       ├── 2018_04_09.md
│       ├── 2018_04_16.md
│       ├── 2018_04_23.md
│       ├── 2018_04_30.md
│       ├── 2018_05_07.md
│       ├── 2018_05_14.md
│       ├── 2018_05_28.md
│       ├── 2018_07_10.md
│       ├── 2018_07_16.md
│       ├── 2018_07_23.md
│       ├── 2018_07_31.md
│       ├── 2018_08_21.md
│       ├── 2018_08_28.md
│       ├── 2018_09_14.md
│       └── 2018_10_02.md
├── memo_core/
│   ├── Cargo.toml
│   ├── README.md
│   ├── rustfmt.toml
│   ├── script/
│   │   └── compile_flatbuffers
│   └── src/
│       ├── btree.rs
│       ├── buffer.rs
│       ├── epoch.rs
│       ├── lib.rs
│       ├── operation_queue.rs
│       ├── serialization/
│       │   ├── mod.rs
│       │   ├── schema.fbs
│       │   └── schema_generated.rs
│       ├── time.rs
│       └── work_tree.rs
├── memo_js/
│   ├── .npmignore
│   ├── .nvmrc
│   ├── Cargo.toml
│   ├── README.md
│   ├── package.json
│   ├── rustfmt.toml
│   ├── script/
│   │   └── build
│   ├── src/
│   │   ├── index.ts
│   │   ├── lib.rs
│   │   └── support.ts
│   ├── test/
│   │   ├── tests.ts
│   │   └── tsconfig.json
│   ├── tsconfig.json
│   └── webpack.config.js
├── rust-toolchain
├── script/
│   ├── bench
│   ├── build
│   ├── cibuild
│   └── test
├── xray_browser/
│   ├── README.md
│   ├── package.json
│   ├── script/
│   │   ├── build
│   │   └── server
│   ├── src/
│   │   ├── client.js
│   │   ├── ui.js
│   │   └── worker.js
│   └── static/
│       └── index.html
├── xray_cli/
│   ├── Cargo.toml
│   ├── README.md
│   └── src/
│       └── main.rs
├── xray_core/
│   ├── Cargo.toml
│   ├── README.md
│   ├── benches/
│   │   └── bench.rs
│   └── src/
│       ├── app.rs
│       ├── buffer.rs
│       ├── buffer_view.rs
│       ├── cross_platform.rs
│       ├── file_finder.rs
│       ├── fs.rs
│       ├── fuzzy.rs
│       ├── lib.rs
│       ├── movement.rs
│       ├── never.rs
│       ├── notify_cell.rs
│       ├── project.rs
│       ├── rpc/
│       │   ├── client.rs
│       │   ├── messages.rs
│       │   ├── mod.rs
│       │   └── server.rs
│       ├── stream_ext.rs
│       ├── tree.rs
│       ├── wasm_logging.rs
│       ├── window.rs
│       └── workspace.rs
├── xray_electron/
│   ├── .gitignore
│   ├── README.md
│   ├── index.html
│   ├── lib/
│   │   ├── main_process/
│   │   │   └── main.js
│   │   ├── render_process/
│   │   │   └── main.js
│   │   └── shared/
│   │       └── xray_client.js
│   └── package.json
├── xray_server/
│   ├── Cargo.toml
│   ├── README.md
│   └── src/
│       ├── fs.rs
│       ├── json_lines_codec.rs
│       ├── main.rs
│       ├── messages.rs
│       └── server.rs
├── xray_ui/
│   ├── README.md
│   ├── lib/
│   │   ├── action_dispatcher.js
│   │   ├── app.js
│   │   ├── debounce.js
│   │   ├── file_finder.js
│   │   ├── index.js
│   │   ├── modal.js
│   │   ├── text_editor/
│   │   │   ├── shaders.js
│   │   │   ├── text_editor.js
│   │   │   └── text_plane.js
│   │   ├── theme_provider.js
│   │   ├── view.js
│   │   ├── view_registry.js
│   │   └── workspace.js
│   ├── package.json
│   └── test/
│       ├── action_dispatcher.test.js
│       ├── file_finder.test.js
│       ├── helpers/
│       │   └── component_helpers.js
│       ├── modal.test.js
│       ├── view.test.js
│       └── view_registry.test.js
└── xray_wasm/
    ├── .gitignore
    ├── Cargo.toml
    ├── lib/
    │   ├── main.js
    │   └── support.js
    ├── package.json
    ├── script/
    │   ├── build
    │   └── test
    ├── src/
    │   └── lib.rs
    └── test/
        └── tests.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
**/node_modules
**/target/
**/*.rs.bk
**/.DS_Store
**/.cargo
Icon*
.tags*
xray_wasm/dist
xray_browser/dist
memo_js/dist
memo_js/test/dist


================================================
FILE: .travis.yml
================================================
language: rust

before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH="$HOME/.yarn/bin:$PATH"
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
- nvm install v11

# Create a virtual display for electron
- export DISPLAY=':99.0'
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &

script: script/cibuild

cache:
  cargo: true
  yarn: true

branches:
  only:
    - master

notifications:
  email:
    on_success: never
    on_failure: change


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Xray

This project is still in the very early days, and isn't going to be usable for even basic editing for some time. At this point, we're looking for contributors that are willing to roll up their sleeves and solve problems. Please communicate with us however it makes sense, but in general opening a *pull request that fixes an issue* is going to be far more valuable than just reporting an issue.

As the architecture stabilizes and the surface area of the project expands, there will be increasing opportunities to help out. To get some ideas for specific projects that could help in the short term, check out [issues that are labeled "help wanted"](https://github.com/atom/xray/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22). If you have an idea you'd like to pursue outside of these, that's awesome, but you may want to discuss it with us in an issue first to ensure it fits before spending too much time on it.

It's really important to us to have a smooth on-ramp for contributors, and one great way you can contribute is by helping us improve this guide. If your experience is bumpy, can you open a pull request that makes it smoother for the next person?

## Communicating with maintainers

The best way to communicate with maintainers is by posting a issue to this repository. The more thought you put into articulating your question or idea, the more value you'll be adding to the community and the easier it will be for maintainers to respond. That said, just try your best. If you have something you want to say, we'd prefer that you say it imperfectly rather than not saying it at all.

You can also communicate with maintainers or other community members on the `#xray` channel on Atom's public slack instance. After you [request an invite via this form](http://atom-slack.herokuapp.com/), you can access our Slack instance at https://atomio.slack.com.

## Building

So far, we have only built this project on macOS. If you'd like to help us improve our build or documentation to support other platforms, that would be a huge help!

### Install system dependencies

#### Install Node v8.9.3

To install Node, you can install [`nvm`](https://github.com/creationix/nvm) and then run `nvm install v8.9.3`.

Later versions may work, but you should ideally run the build with the same version of Node that is bundled into Xray's current Electron dependency. If in doubt, you can check the version of the `electron` dependency in [`xray_electron/package.json`](https://github.com/atom/xray/blob/master/xray_electron/package.json), then run `process.versions.node` in the console of that version of Electron to ensure that these instructions haven't gotten out of date.

#### Install Rust

You can install Rust via [`rustup`](https://www.rustup.rs/). We currently require building on the nightly channel in order to use `wasm_bindgen` for browser support.

#### Install Yarn

Follow the [installation instructions](https://yarnpkg.com/en/docs/install) on the Yarn site.

### Run the build script

This repository contains several components in top-level folders prefixed with `xray_*`. To build all of the components, simply run this in the root of the repository:

```sh
script/build
```

To build a release version (which will be much faster):

```sh
script/build --release
```

## Running

We currently *only* support launching the application via the CLI. For this to work, you need to set the `XRAY_SRC_PATH` environment variable to the location of your repository. The CLI also currently *requires* an argument:

```sh
XRAY_SRC_PATH=. script/xray .
```

That assumes you built with `--release`. To run the debug version, use `xray_debug` instead:

```sh
XRAY_SRC_PATH=. script/xray_debug .
```

Once a blank window has opened, press <kbd>cmd-t</kbd> to open the file selection menu. Search for a file, and press <kbd>enter</kbd> to open it. The contents of the file should appear in the window. If something does not go as expected, check the dev tools (<kbd>cmd-shift-i</kbd>) for errors.

## Running tests and benchmarks

* All tests: `script/test`
* Rust tests: `cargo test` in the root of the repository or a Rust subfolder.
* Front-end tests: `cd xray_ui && yarn test`
* Benchmarks: `cargo bench`


================================================
FILE: Cargo.toml
================================================
[workspace]
members = [
    "memo_core",
    "memo_js",
    "xray_core",
    "xray_server",
    "xray_cli",
    "xray_wasm",
]


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 GitHub

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
**Attention:** GitHub has decided not to move forward with any aspect of this project. We'll archive the repository in case anybody finds value here, but we don't expect to actively work on this in the foreseeable future. Thanks to everyone for their interest and support.

# Xray

[![Build Status](https://travis-ci.org/atom/xray.svg?branch=master)](https://travis-ci.org/atom/xray)

Xray is an experimental Electron-based text editor informed by what we've learned in the four years since the launch of Atom. In the short term, this project is a testbed for rapidly iterating on several radical ideas without risking the stability of Atom. The longer term future of the code in this repository will become clearer after a few months of progress. For now, our primary goal is to iterate rapidly and learn as much as possible.

## Q3 2018 Focus

We're currently focused on a sub-project of Xray called [Memo](./memo_core), which will serve as the foundation of Xray but also be available as a standalone tool. Memo is an operation-based version control system that tracks changes at the level of individual keystrokes and synchronizes branches in real time.

## Updates

* [October 2, 2018](./docs/updates/2018_10_02.md)
* [September 14, 2018](./docs/updates/2018_09_14.md)
* [August 28, 2018](./docs/updates/2018_08_28.md)
* [August 21, 2018](./docs/updates/2018_08_21.md)
* [July 31, 2018](./docs/updates/2018_07_31.md)
* [July 23, 2018](./docs/updates/2018_07_23.md)
* [July 16, 2018](./docs/updates/2018_07_16.md)
* [July 10, 2018](./docs/updates/2018_07_10.md)
* [Archives](./docs/updates/)

## Foundational priorities

Our goal is to build a cross-platform text editor that is designed from the beginning around the following foundational priorities:

### Collaboration

*Xray makes it as easy to code together as it is to code alone.*

We design features for collaborative use from the beginning. Editors and other relevant UI elements are designed to be occupied by multiple users. Interactions with the file system and other resources such as subprocesses are abstracted to work over network connections.

### High performance

*Xray feels lightweight and responsive.*

We design our features to be responsive from the beginning. We reliably provide visual feedback within the latency windows suggested by the [RAIL performance model](https://developers.google.com/web/fundamentals/performance/rail). For all interactions, we shoot for the following targets on the hardware of our median user:

| Duration | Action |    
| - | - |
| 8ms | Scrolling, animations, and fine-grained interactions such as typing or cursor movement. |
| 50ms | Coarse-grained interactions such as opening a file or initiating a search. If we can't complete the action within this window, we should show a progress bar. |
| 150ms | Opening an application window. |

We are careful to maximize throughput of batch operations such as project-wide search. Memory consumption is kept within a low constant factor of the size of the project and open buffer set, but we trade memory for speed and extensibility so long as memory requirements are reasonable.

### Extensibility

*Xray gives developers control over their own tools.*

We expose convenient and powerful APIs to enable users to add non-trivial functionality to the application. We balance the power of our APIs with the ability to ensure the responsiveness, stability, and security of the application as a whole. We avoid leaking implementation details and use versioning where possible to enable a sustained rapid development without destabilizing the package ecosystem.

### Web compatibility

*Editing on GitHub feels like editing in Xray.*

We want to provide a full-featured editor experience that can be used from within a browser. This will ultimately help us provide a more unified experience between GitHub.com and Xray and give us a stronger base of stakeholders in the core editing technology.

## Architecture

Martin Fowler defines software architecture as those decisions which are both important and hard to change. Since these decisions are hard to change, we need to be sure that our foundational priorities are well-served by these decisions.

![Architecture](docs/images/architecture.png)

### The UI is built with web technology

Web tech adds a lot of overhead, which detracts from our top priority of high-performance. However, web standards are also the best approach that we know of to deliver a cross-platform, extensible user interface. Atom proved that developers want to add non-trivial UI elements to their editor, and we still see web technologies as the most viable way to offer them that ability.

The fundamental question is whether we can gain the web's benefits for extensibility while still meeting our desired performance goals. Our hypothesis is that it's possible–with the right architecture.

### Core application logic is written in Rust

While the UI will be web-based, the core of the application is implemented in a server process written in Rust. We place as much logic as possible in a library crate located in `/xray_core`, then expose this logic as a server when running Xray on the desktop (`/xray_server`) and a web-assembly library running on a worker thread when running Xray in the browser (`/xray_wasm`). We communicate between the UI and the back end process via JSON RPC.

All of the core application code other than the view logic should be written in Rust. This will ensure that it has a minimal footprint to load and execute, and Rust's robust type system will help us maintain it more efficiently than dynamically typed code. A language that is fundamentally designed for multi-threading will also make it easier to exploit parallelism whenever the need arises, whereas JavaScript's single-threaded nature makes parallelism awkward and challenging.

Fundamentally, we want to spend our time writing in a language that is fast by default. It's true that it's possible to write slow Rust, and also possible to write fast JavaScript. It's *also* true that it's much harder to write slow Rust than it is to write slow JavaScript. By spending fewer resources on the implementation of the platform itself, we'll make more resources available to run package code.

### I/O will be centralized in the server

The server will serialize buffer loads and saves on a per-path basis, and maintains a persistent database of CRDT operations for each file. As edits are performed in windows, they will be streamed to the host process to be stored and echoed out to any other windows with the same open buffer. This will enable unsaved changes to always be incrementally preserved in case of a crash or power failure and preserves the history associated with a file indefinitely.

Early on, we should design the application process to be capable of connecting to multiple workspace servers to facilitate real-time collaboration or editing files on a remote server by running a headless host process. To support these use cases, all code paths that touch the file system or spawn subprocesses will occur in the server process. The UI will not make use of the I/O facilities provided by Electron, and instead interact with the server via RPC.

### Packages will run in a JavaScript VM in the server process

A misbehaving package should not be able to impact the responsiveness of the application. The best way to guarantee this while preserving ease of development is to activate packages on their own threads. We can run a worker thread per package or run packages in their own contexts across a pool of threads.

Packages *can* run code on the render thread by specifying versioned components in their `package.json`.

```json
"components": {
  "TodoList": "./components/todo-list.js"
}
```

If a package called `my-todos` had the above entry in its `package.json`, it could request that the workspace attach that component by referring to `myTodos.TodoList` when adding an item. During package installation on the desktop, we can automatically update the V8 snapshot of the UI to include the components of every installed package. Components will only be dynamically loaded from the provided paths in development mode.

Custom views will only have access to the DOM and an asynchronous channel to communicate with the package's back end running on the server. APIs for interacting with the core application state and the underlying operating system will only be available within the server process, discouraging package authors from putting too much logic into their views. We'll use a combination of asynchronous channels and CRDTs to present convenient APIs to package authors within worker threads.

### Text is stored in a copy-on-write CRDT

To fully exploit Rust's unique advantage of parallelism, we need to store text in a concurrency-friendly way. We use a variant of RGA called RGASplit, which is described in [this research paper](https://pages.lip6.fr/Marc.Shapiro/papers/rgasplit-group2016-11.pdf).

![CRDT diagram](docs/images/crdt.png)

In RGA split, the document is stored as a sequence of insertion fragments. In the example above, the document starts as just a single insertion containing `hello world`. We then introduce `, there` and `!` as additional insertions, splitting the original insertion into two fragments. To delete the `ld` at the end of `world` in the third step, we create another fragment containing just the `ld` and mark it as deleted with a tombstone.

Structuring the document in this way has a number of advantages.

* Real-time collaboration works out of the box
* Concurrent edits: Any thread can read or write its own replica of the document without diverging in the presence of concurrent edits.
* Integrated non-linear history: To undo any group of operations, we increment an undo counter associated with any insertions and deletions that controls their visibility. This means we only need to store operation ids in the history rather than operations themselves, and we can undo any operation at any time rather than adhering to historical order.
* Stable logical positions: Instead of tracking the location of markers on every edit, we can refer to stable positions that are guaranteed to be valid for any future buffer state. For example, we can mark the positions of all search results in a background thread and continue to interpret them in a foreground thread if edits are performed in the meantime.

Our use of a CRDT is similar to the Xi editor, but the approach we're exploring is somewhat different. Our current understanding is that in Xi, the buffer is stored in a rope data structure, then a secondary layer is used to incorporate edits. In Xray, the fundamental storage structure of all text is itself a CRDT. It's similar to Xi's rope in that it uses a copy-on-write B-tree to index all inserted fragments, but it does not require any secondary system for incorporating edits.

### Derived state will be computed asynchronously

We should avoid implementing synchronous APIs that depend on open-ended computations of derived state. For example, when soft wrapping is enabled in Atom, we synchronously update a display index that maps display coordinates to buffer coordinates, which can block the UI.

In Xray, we want to avoid making these kinds of promises in our API. For example, we will allow the display index to be accessed synchronously after a buffer edit, but only provide an interpolated version of its state that can be produced in logarithmic time. This means it will be spatially consistent with the underlying buffer, but may contain lines that have not yet been soft-wrapped.

We can expose an asynchronous API that allows a package author to wait until the display layer is up to date with a specific version of the buffer. In the user interface, we can display a progress bar for any derived state updates that exceed 50ms, which may occur when the user pastes multiple megabytes of text into the editor.

### React will be used for presentation

By using React, we completely eliminate the view framework as a concern that we need to deal with and give package authors access to a tool they're likely to be familiar with. We also raise the level of abstraction above basic DOM APIs. The risk of using React is of course that it is not standardized and could have breaking API changes. To mitigate this risk, we will require packages to declare which version of React they depend on. We will attempt using this version information to provide shims to older versions of React when we upgrade the bundled version. When it's not possible to shim breaking changes, we'll use the version information to present a warning.

### Styling will be specified in JS

CSS is a widely-known and well-supported tool for styling user interfaces, which is why we embraced it in Atom. Unfortunately, the performance and maintainability of CSS degrade as the number of selectors increases. CSS also lacks good tools for exposing a versioned theming API and applying programmatic logic such as altering colors. Finally, the browser does not expose APIs for being notified when computed styles change, making it difficult to use CSS as a source of truth for complex components. For a theming system that performs well and scales, we need more direct control. We plan to use a CSS-in-JS approach that automatically generates atomic selectors so as to keep our total number of selectors minimal.

### Text is rendered via WebGL

In Atom, the vast majority of computation of any given frame is spent manipulating the DOM, recalculating styles, and performing layout. To achieve good text rendering performance, it is critical that we bypass this overhead and take direct control over rendering. Like Alacritty and Xi, we plan to employ OpenGL to position quads that are mapped to glyph bitmaps in a texture atlas.

There isn't always a 1:1 relationship between code units inside a JavaScript string and glyphs on screen. Characters (code points) can be expressed as two 16-bit units, but this situation is simple to detect by examining the numeric ranges of the code units. In other cases, the correspondence between code units and glyphs is less straightforward to determine. If the current font and/or locale depends on ligatures or contextual alternates to render correctly, determining the correspondence between code points and glyphs requires support for complex text shaping that references metadata embedded in the font. Bi-directional text complicates the situation further.

For now, our plan is to detect the presence of characters that may require such complex text shaping and fall back to rendering with HTML on the specific lines that require these features. This will enable us to support scripts such as Arabic and Devanagari. For fonts like FiraCode, which include ligatures for common character sequences used in programming, we'll need a different approach. One idea would be to perform a limited subset of text-shaping that just handles ligatures, so as to keep performance high. Another approach that would only work on the desktop would be to use the platform text-shaping and rasterization APIs in this environment.

Bypassing the DOM means that we'll need to implement styling and text layout ourselves. That is a high price to pay, but we think it will be worth it to bypass the performance overhead imposed by the DOM.

## Development process

### Experiment

At this phase, this code is focused on learning. Whatever code we write should be production-quality, but we don't need to support everything at this phase. We can defer features that don't contribute substantially to learning.

### Documentation-driven development

Before coding, we ask ourselves whether the code we're writing can be motivated by something that's written in the guide. The right approach here will always be a judgment call, but let's err on the side of transparency and see what happens.

### Disciplined monorepo

All code related to Xray should live in this repository, but intra-repository dependencies should be expressed in a disciplined way to ensure that a one-line docs change doesn't require us to rebuild the world. Builds should be finger-printed on a per-component basis and we should aim to keep components granular.

## Contributing

Interested in helping out? Welcome! Check out the [CONTRIBUTING](./CONTRIBUTING.md) guide to get started.


================================================
FILE: docs/architecture/001_client_server_protocol.md
================================================
# Xray's client/server protocol

Xray is organized around a client/server architecture, with all the application logic located in a central server. User-facing components connect to this server as clients to present the user experience.

## Major application components

![Major components](../images/client_server_components.png)

All application logic is controlled by a single server that listens on a domain socket located at `ATOM_SOCKET_PATH`. We connect to the server with three different types of clients:

* **CLI:** When you run the `xray` binary, we will check if a socket for the server already exists and is listening. If it does, we will connect to this socket and communicate with the server directly. For example, the application may already be running, but we want to open a new workspace for a given path. To do that, we just connect to the existing socket and send it an `OpenWorkspace` message. If the CLI is unable to connect to the socket, it spawns the Electron app and waits for it to report that it is `Listening\n` on `stdout`.
* **App:** The Electron app in `xray_electron` spawns the server as a child process on startup and identifies itself as the application client via the `{type: "StartApp"}` message. The server then sends the app client application-level command messages like the `OpenWindow` message, which tells the app to open a new window.
* **Window:** When the server tells the app to open a window, it provides a window id, which gets passed to the Electron window in the URL. Once the window loads, it connects to the server's socket and identifies itself as a window, supplying this id.

## The window protocol

![Window protocol diagram](../images/window_protocol.png)

The protocol between the window and the server is inspired by the [Flux application architecture](https://facebook.github.io/flux/), though it's probably different in some ways due to the particular needs of Xray.

The state of the UI for any given window is managed entirely by the server. It creates a `Window` object for each connected window, and this `Window` object is responsible for managing a tree of views to be rendered by the connected client. Each view is associated with a unique identifier, a component name, and a plain-old JS object representing the view's state. Views can refer to *other* views via their id.

When views are added and removed from the `Window` object on the server side, updates are automatically relayed to the client. The server calls `render()` on any newly added views to obtain a JSON object representing the view's state. The window also observes an `updates()` stream associated with each view, and sends a new update for a view's state if the view becomes dirty. To keep things simple, each time a view is updated, its entire state tree is sent again across the wire. For this reason, it's important to limit the size of each view's state object to avoid transmission and parsing overhead. Since the required data for a given view is naturally limited by the viewport, this should be acceptable. We may switch to protocol buffers if JSON parsing overhead becomes a bottleneck.

The root view of a typical window is a `WorkspaceView` with an id of `0`. Its props refer to other views that are displayed in the workspace via their id. For example, the workspace may contain a `BufferView` (editor) with id 1, and also be presenting a `FileFinderView` with id 2 as a modal panel. When views are added to the `Window`, they are provided with a `WindowHandle` via the optional `did_mount` method that allows them to add additional sub-views to the window. When a view adds a sub-view, it receives a `ViewHandle`. When this handle is dropped, the sub-view is automatically removed from the `Window` and deleted on the client.

In the render process, we maintain a `ViewRegistry` which mirrors the state of the `Window` in the server process. The `ViewRegistry` contains an imperative interface for fetching the component and props associated with a particular view id, although most code will interface with the registry declaratively via special React components.

The render process communicates changes to the server via *actions*, which are plain-old JS objects that can be dispatched to a particular view id. These actions are dispatched from the `ViewRegistry` on the render process and make their way to the server, where they are routed by the `Window` object. `Window` calls `dispatch_action` on the view corresponding to the action's specified `view_id`, passing the JSON to the view for handling.

Views can handle an action by updating their own state or the state of other model objects. The `Window` detects state changes via the `updates` stream of any current views, then sends these updates to the client.

The server can also tell the window to *focus* a particular view by calling the `focus` method on its top-level React component. This can be accessed via the `ViewHandle::focus` method on the server side. These commands are simply relayed to the client. The server has no explicit model of focus state.

## Detecting when views need to be re-rendered

Each view is associated with an `updates` stream, which is implemented with the Rust [`futures`](https://docs.rs/futures/0.2.0-alpha/futures/) crate. A full explanation of Rust futures is beyond the scope of this document, but their poll-oriented nature is relevant to this use case.

The `Window` object represents the messages that need to be sent to the client as a `Stream` called `WindowUpdateStream`. This stream implements `poll`, which checks dirty sets for inserted and removed views, then calls `poll` on the updates stream of all currently-visible views. Any views returning `Async::Ready` are then rendered and added to the next update to be sent to the client.

If polling every visible view on each poll of the window update stream turns out to have too much overhead, we can always employ a similar strategy to the `FuturesUnordered` object and track notifications in a more fine-grained way. However, since we're anticipating rendering far fewer than 100 discrete views at any one time, we don't think polling everything should be an issue.

One cool feature of the stream-oriented approach for detecting individual view updates is that a given view's update stream could be composed from other streams in fairly complex ways. For example, the updates stream of a `BufferView` (editor) could derive from a `NotifyCell` on the view itself, plus an updates stream on the view's `Buffer`, which it could share with other `BufferView`s.

## Declarative interface on the client

To consume view state on the client, we implement a `ViewRegistry` that allows you to get the component and props for any view id, and also watch those props for updates.

The `ViewRegistry`'s imperative API is wrapped in a component-oriented interface. At the root of the component hierarchy is the `App` component, which injects the view registry into the component tree's [context](https://reactjs.org/docs/context.html). Beneath the `App` component, the `View` component can be used to render a view with a specific id.

The `View` component takes the view's `id` as a property, then retrieves the view's component and props from the registry and renders the component as a child. It also sets up an observer on the view's props, re-rendering its child component with the new properties when they change. Finally, the `View` component passes a `dispatch` method as a property to the child component, giving it the ability to send arbitrary actions as plain JS objects back to the view's server-side representation.


================================================
FILE: docs/architecture/002_shared_workspaces.md
================================================
# Shared workspaces

## Current features

An instance of `xray_server` can host one or more shared workspaces, which can be accessed by other `xray_server` instances over the network. Currently, when connecting to a remote peer, we automatically open its first shared workspace in a window on the client. The client can use the file finder to locate and open any file in the shared workspace's project. When multiple participants open a buffer for the same file, their edits are replicated to other collaborators in real time.

### Server

* `xray foo/ bar/ --listen 8888` starts the Xray server listening on port 8888.
* The `--headless` flag can be passed to create a server that only hosts workspaces for other clients and does not present its own UI.

### Basic client experience

* `xray --connect hostname:port` opens a new window that is connected to the first workspace available on the remote host.
* `cmd-t` in the new window searches through paths in the remote workspace.
* Selecting a path opens it.
* Running `xray --connect` from a second instance allows for collaborative editing when clients open the same buffer.

### Selecting between multiple workspaces on the client

* If the host exposes multiple workspaces, `xray --connect hostname:port` opens an *Open Workspace* dialog that allows the user to select which workspace to open.
* `cmd-o` in any Xray window opens the *Open Workspace* dialog listing workspaces from all connected servers.

## RPC System

We implement shared workspaces on top of an RPC system that allows objects on the client to derive their state and behavior from objects that live on the server.

### Goals

#### Support replicated objects

The primary goal of the system is to support the construction of a replicated object-oriented domain model. In addition to supporting remote procedure calls, we also want the system to explicitly support long-lived, stateful objects that change over time.

Replication support should be fairly additive, meaning that the domain model on the server side should be designed pretty much as if it weren't replicated. On the client side, interacting with representations of remote objects should be explicit but convenient.

#### Capabilities-based security

Secure ECMA Script and Cap'N Proto introduced me to the concept of capabilities-based security, and our system adopts the same philosophy. Objects on the server are exposed via *services*, which can be thought of as "capabilities" that grant access to a narrow slice of functionality that is dynamically defined. Starting from a single root service, remote users are granted increasing access by being provided with additional capabilities.

#### Dynamic resource management

Server-side services only need to live as long as they are referenced by a client. Server-side code can elect to retain a reference to a service. Otherwise, ownership is maintained by clients over the wire. If both the server and the client drop their reference-counted handle to a service, we should drop the service on the server side automatically.

#### Binary messages

We want to move data efficiently between the server and client, so a binary encoding scheme for messages is important. For now, we're using bincode for convenience, but we should eventually switch to Protocol Buffers to support graceful evolution of the protocol.

### Design

![Diagram](../images/rpc.png)

**Services** are the fundamental abstraction of the system.

In `rpc::server`, `Service` is a *trait* that can be implemented by a custom service wrapper for each domain object that makes the object accessible to remote clients. A `Service` exposes a static snapshot of the object's initial state, a stream of updates, and the ability to handle requests. The `Service` trait has various associated types for `Request`, `Response`, `Update`, and `State`.

When server-side code accepts connections, it creates an `rpc::server::Connection` object for each client that takes ownership of the `Stream` of that client's incoming messages. `Connection`s must be created with a *root service*, which is sent to the client immediately. The `Connection` is itself a `Stream` of outgoing messages to be sent to the connected client.

On the client side, we create a connection by passing the `Stream` of incoming messages to `rpc::client::Connection::new`, which returns a *future* for a tuple containing two objects. The first object is a `rpc::client::Service` representing the *root service* that was sent from the server. The second is an instance of `client::Connection`, which is a `Stream` of outgoing messages to send to the server.

Using the root service, the client can make requests to gain access to additional services. In Xray, the root service is currently `app::AppService`, which includes a list of shared workspaces in its replicated state. After a client connects to a server, it stores a handle to its root service in a `PeerList` object. We will eventually build a `PeerListView` based on the state of the `PeerList`, which allows the user to open a remote workspace on any connected peer. For now, we automatically open the first workspace when connecting to a remote peer.

When we connect to a remote workspace, we send an `app::ServiceRequest::OpenWorkspace` message to the remote `AppService`. When handling this request in the `AppService` on the server, we call `add_service` on the connection with a `WorkspaceService` for the requested workspace, which returns us a `ServiceId` integer. We send that id to the client in the response. When handling the response on the client, we call `take_service` on root service with the id to take ownership of a handle to the remote service.

We can then create a `RemoteWorkspace` and pass it ownership of the service handle to the remote workspace. `RemoteWorkspace` and `LocalWorkspace` both implement the `Workspace` trait, which allows a `RemoteWorkspace` to be used in the system in all of the same ways that a `LocalWorkspace` can.

We create the illusion that remote domain objects are really local through a combination of state replication and remote procedure calls. Fuzzy finding on the project file trees is addressed through replication, since the data size is typically small and the task is latency sensitive. Project-wide search is implemented via RPC, since replicating the contents of the entire remote file system would be costly, especially for the in-browser use case. Buffer replication is implemented by relaying conflict-free representations of individual edit operations, which can be correctly integrated on remote replicas due to our use of a CRDT in Xray's underlying buffer implementation.


================================================
FILE: docs/architecture/003_memo_epochs.md
================================================
The following document describes the sequence of operations that we should perform when the repository HEAD changes, both on the machine where the HEAD change occurred and at remote sites that receive the resulting epoch change.

The algorithms assume that version vectors don't reset across epochs. This does raise the concern that version vectors could grow without bound over the life of the repository, but we're going to suspend that concern temporarily to make progress.

### Creating a new epoch after HEAD moves

Assume we are currently at epoch A described by Tree T.

- Scan all the entries from Git's database based on the new HEAD into a new Tree T'.
- Synthesize and apply operations for all uncommitted changes via a `git diff`. This includes file system operations as well as uncommitted changes to file contents.
- For all buffers with unsaved edits in T:
  - Diff the last saved contents in T against the current contents of T' using the path of the buffer in T. This diff will describe a set of regions that have been touched outside of our control.
  - Go through each of the unsaved operations in T and check if they intersect with any of the regions in this diff to detect a conflict.
    - If there is a conflict, synthesize operations by performing a diff between the contents of T' and the contents of T and apply these as unsaved operations on top of T', then mark the buffer as in conflict.
    - Otherwise, transform all the unsaved operations according to the initial diff and apply them to the buffer in T'.

Afterward, we broadcast a new epoch B that contains the new HEAD SHA, the work tree's current version vector, a Lamport timestamp, and all synthesized operations.

### Receiving a new epoch

* Check Lamport timestamp of the epoch. If it's less than the current epoch's timestamp, ignore it. Otherwise, proceed to change the active epoch as follows:
  * Scan all entries from Git's database based on the new epoch's HEAD SHA into a new Tree T'.
  * Apply operations that are associated with the new epoch to T'.

What happens to buffers?
  * For all buffers containing edits not included in the epoch change's version vector:
    * If a file with the same path exists in T':
      * Diff the contents that are included in the version vector against the contents of T' using the path of the buffer in T. This diff will describe a set of regions that have been touched outside of our control.
      * Go through each of the local edits that were not part of the version vector. If they do not directly conflict with a region in the diff, synthesize a new operation with an adjusted position based on the diff and apply it to T'.
    * If no file with that path exists in T', we create it with initial contents from T.


================================================
FILE: docs/updates/2018_03_05.md
================================================
# Update for March 5, 2018

## Contributions

We received some great contributions from [@dirk](https://github.com/dirk) that improved error handling ([#5](https://github.com/atom/xray/pull/5)) and refined how we build our N-API bindings ([#7](https://github.com/atom/xray/pull/7), [#9](https://github.com/atom/xray/pull/9)). He also clarified our build process in the documentation and added an explicit Electron dependency now that the new beta supports N-API ([#10](https://github.com/atom/xray/pull/10)). Thanks @dirk!

## 12-week experiment

Our plan is to dedicate 12 full weeks to Xray and see how far we can get with the implementation. We originally planned to start this trial period 2 weeks ago, but decided to defer it in order to spend more time doing planning around our vision for real time collaboration. So last week will count as week 1 of 12. This week is week 2 of 12.

## Text shaping

We're currently rendering text with a fairly naive strategy, where we just transform code points to glyphs and position them one after another with WebGL. The great thing about this strategy is it's really fast. It takes me ~1.2 ms to render a full screen's worth of text on a late 2016 MacBook Pro. The downside of this strategy is that we don't perform correct text shaping.

Last week, we explored running all of our lines through HarfBuzz compiled as a separate WebAssembly module, but in our tests, running HarfBuzz on 50 lines of 100 characters each was taking between 4.5ms and 20ms, depending on the font. Since our target for a frame is 8ms, this makes us pretty reluctant to pursue this path further. We're a code editor, not a word processor, so it's not clear that we *need* all the features that a full-on text shaping engine provides.

If we don't do some sort of text shaping (and HarfBuzz seems like the only game in town), here's what we'll be missing:

* No ligatures support: Text shapers combine code points with tables embedded in the font to decide when to render ligatures. We're a code editor, so this isn't a deal-breaker, but fonts like Fira Code rely on ligatures to render special characters for common programming sequences such as `<=`.
* No kerning support: For fixed width fonts, we weren't able to observe any noticeable difference for a lack of kerning. For variable-width fonts like Helvetica, rendering without kerning looks a bit odd. Again, we're a code editor, so not a deal-breaker.
* No support for bi-directional text. This isn't a deal-breaker in the short term, since all of the dominant programming languages are based on latin scripts. Again, it's not our ambition to become a general word processor. Long term, however, we need to support right-to-left text appearing in strings and comments in order to be usable by developers working with languages like Arabic and Hebrew. Interestingly, Sublime Text does not appear to support bidirectional text, but we'd like to do better.
* No support for context-sensitive substitutions. In Arabic and Indic scripts, the same characters can render different glyphs depending on their context. Sublime also does not support this.

Based on what we have learned and the above limitations, this our plan for text shaping going forward. In the near-term:

* Don't support full text shaping in the general case. We want to emphasize maximal speed for the common case, which shouldn't require full text shaping. If running text shaping on every line took less than 1ms, it would be worth it, but we'd prefer not to pay what it appears to cost.

At some point in the future, make the following enhancements:

* Add bi-directional text support. We've run into trouble building a library that combines both Rust and C/C++ in a single WebAssembly module, so the ideal path would be to find or write an implementation of the Unicode bi-directional text algorithm in Rust and embed it in `xray_core`. One important detail is that we need to preserve the correspondence between column positions in the source and rendered text in order to render cursors and interpret mouse interactions, so just transforming the text alone will be insufficient.
* Use presentational characters to render Arabic [as described in this blog post](https://blog.mapbox.com/improving-arabic-and-hebrew-text-in-map-labels-fd184cf5ebd1), again porting an existing implementation of this transformation to Rust and incorporating it into `xray_core`. Again, we'll need to maintain a mapping of how characters in the input and the output map for cursor positioning. Several of the existing implementations of this transformation are GPL-licensed, so we'll need to be careful to avoid deriving our work from one of them.
* Add limited ligatures support at some point in the future to `xray_core`. This would involve loading the font and consulting the lookup table for ligatures. The goal would be support for fonts like Fira Code, and the hope is that we will be able to efficiently perform just this subset of the generalized text shaping workload within our budget of 1ms.
* Render sequences of Indic characters as atomic units via canvas rather than trying to render and composite individual glyphs like we do for other scripts. This would rely on the text-shaping built into the browser to render words in these scripts. We will pay a performance cost, but since we're anticipating these characters to appear rarely as part of comments and strings, it should be acceptable and better than adding the performance and complexity of full shaping for cases where it isn't needed.

Producing a lightning fast editor that runs on the web is going to involve trade-offs, and we'll need to make some tough decisions. Avoiding full text shaping is one of them. It would be great to be fast *and* perfectly correct in all cases, but we're not willing to sacrifice speed in the common case for perfect correctness at the corners.

We're going to post some help-wanted issues to see if anyone is interested in helping out with some of the compromise solutions in the above plan.

So in conclusion, we didn't end up merging any *code* related to text shaping, but we did learn a lot and came up with a clear plan for how to proceed.

## Anchors and selections

The bulk of the week was spent adding support for selections to the editor. The first step was an introduction of a new abstraction called *anchors*. Anchors serve a similar role to markers in Atom today, but they have a much cleaner implementation due to the buffer being a CRDT.

An anchor is a *value* that tracks a logical position in a buffer. You create an anchor by calling one of the following methods on the buffer:

* `anchor_before_offset`
* `anchor_after_offset`
* `anchor_before_point`
* `anchor_after_point`

These return an opaque `Anchor` value, which can be converted back to a concrete offset or point in the future via the following methods:

* `offset_for_anchor`
* `point_for_anchor`

Internally, an anchor is an enum that either represents either the `Start` or `End` of the file or some point in the `Middle` of the file via an `insertion_id`, `offset`, and `bias`. If you create an anchor at offset 10, its position will be updated by any edits that occur prior to offset 10, so that it always tracks the same logical position in the text. So if you create this anchor *before* offset 10, it will have a *left* bias and remain at that offset even if there is a subsequent insertion at its exact location. If the anchor is created *after* offset 10, it will have a *right* bias and be pushed rightward by insertions at its location.

Selections are built on top of anchors. Each anchor maintains a vector of selections ordered by their start anchor, maintaining the invariant that the selection ranges are always disjoint. We use anchors for selections rather than absolute positions so that the logical intention of the user is maintained even in the face of edits to the buffer by other users or by packages. We implemented basic cursor movements and selection expansions (up, down, left, and right) as well as methods to add a selection above and below the current.

We plan to render selections and cursors as additional WebGL shader passes that draw solid rectangles. We have the plumbing mostly in place to do this, but haven't finished actually populating the buffers on the GPU to tell the shaders where to draw. We're hoping to have that finished early this week, so we can move on to handling the input to actually move the selections and cursors around. That will raise the question of how we handle key bindings and commands in Xray, which could take some time to iron out.

Once we can render and manipulate selections, we'll move on to handling keystrokes to perform actual edits to the buffer. The `splice` method already exists to enable edits, so it should just be a matter of calling it in a loop in reverse order of the selection ranges. Once we add some caching related to translating anchors to positions, we can measure our performance and see how many cursors we can type with within our 8ms target window. Hopefully we do well.

## The week ahead

We'll be a bit short-handed this week due to @as-cii being on reactive duty for Atom and @nathansobo heading to Denver on Wednesday to give a talk at Pivotal Labs. We hope to finish selection rendering and ideally also get an initial solution in place for key bindings and commands to move those selections around. If things go really well, we'll start on editing.


================================================
FILE: docs/updates/2018_03_12.md
================================================
# Update for March 12, 2018

## Contributions

We got some help from [@apcragg](https://github.com/apcragg), who made [the build script for our N-API bindings support Linux and Windows](https://github.com/atom/xray/pull/25). Also [@maxim-lian](https://github.com/maxim-lian) helped by updating our repo to correctly use [Cargo workspaces](https://github.com/atom/xray/pull/26). Also saw a bunch of people building the project and exploring. There's not much to see yet, but we're happy that people are interested.

## Hacker News and new contributor interest

Someone posted this repo to Hacker News last week, which drew a bunch of attention to the project. Overall this is obviously great, but I sort of regret reading the comments. People can be so mean on HN and it's really a drag to feel like you're the target of vitriol and derision. I'm just over here trying to build stuff. But I guess you gotta just have a thick skin and keep coding.

Thanks to everyone who jumped in with an interest in contributing! The engagement was really helpful and prompted me to post [the beginnings of a contributing guide](https://github.com/atom/xray/blob/master/CONTRIBUTING.md) and some initial [help-wanted issues](https://github.com/atom/xray/labels/help%20wanted). Looking forward to engaging with someone who wants to dive in on one of those problems.

## Progress on selections

We have selections and cursors rendering on a branch, and hope to merge it this week. The results are promising, and we're only exceeding our 8 ms frame budget when we reach thousands of selections in a document with thousands of edits. We think we can do better with some optimizations.

We plan to merge an initial PR that lays the groundwork this week. This will include the ability to model and render selections, but we need to get a basic key bindings and commands system in place before this will mean much in the UI. We'll post some more detailed benchmarks once we get this basic implementation integrated and have a chance to look at the profile for any basic optimizations.

## Big architectural changes incoming

A conversation with [@joefitzgerald](https://github.com/joefitzgerald) at Pivotal in Denver led to a big shift in how we plan to interoperate between JavaScript and Rust. Originally, we planned to embed a shared library written in Rust into the Electron render process and use Node's N-API to interoperate. Somewhere along the line, we realized that to maintain a global edit history for all files that wasn't tied to a project, we would need to unify all of the file system interaction in a single process to avoid race conditions between multiple Electron windows. This process gradually grew in our thinking to take on other responsibilities, such as connecting to other processes to facilitate real time collaboration. Eventually, we decided that we would need to route all I/O through this central process.

Joe asked a simple and insightful question. If we're planning to route all I/O through an external process, then what's the point of the Node APIs available in Electron? They're all designed around I/O, but we won't be doing any. This made it click for me that we should actually move *all* of the application logic completely into the external Rust process and treat the UI as an extremely thin layer that interacts with the app via an async channel. We'll use something akin to the Flux architecture, where the UI can submit actions to the server on the channel and receive JSON payloads representing state to render. Yes, I realize that this makes us even *more* similar to Xi architecturally.

This will make the UI code 100% ordinary HTML and JavaScript with no assumption of any special APIs, which really supports our goal of running on the web. For the desktop experience, we can use any solution that gives us a modern, standards-compliant web view and run the core of the application as a server process. The easiest solution on the desktop would be Electron, but we could even use an embedded WebKit view on the Mac to save on bundle size if we wanted to absorb that complexity (it's not clear it's worth it though). On the web, we'll need the server process to run inside the browser, because this whole design assumes an ultra-low-latency connection between the front end and the back end. In this scenario, we can compile some subset of the server process that runs on the desktop to WebAssembly and run it in a worker thread. This web "back end" can then establish a peer-to-peer connection with another Xray process running in the cloud or on another user's computer. This will provide a 100% compatible experience between the browser and the desktop and enable collaboration between users in either environment.

Here's a somewhat complicated picture of our current thinking. Note the centrality of the "Xray Core Process". All the logic and extensibility lives there, and the view becomes much simpler.

![New architecture](../images/architecture.png)

We'll be starting on these changes this week. The first step is to completely eliminate Electron and wrap `xray_core` in a server process, which we're thinking about calling `xray_server`. The front end will be `xray_client` and compile down to a simple HTML file. When you open it in the browser, it will connect to a local port based on the query parameter in the URL.

Once we accomplish that, we'll need to figure out an alternate variant of the server, maybe called `xray_server_wasm`, which runs a "server" in one or more worker threads for a low-latency experience on the web. Compiling pure Rust to WebAssembly hopefully won't be a big deal, but we are a bit worried about including TreeSitter, whose runtime and grammars are written in C. We think we can figure it out though.

In light of these big structural changes, we'll probably be holding off on merging any PRs this week until the dust has settled. Thanks for your patience.

## What about Xi?

I've mentioned elsewhere in this repo the conversation I had with Raph Levien a few weeks ago about collaborating with Xi. In light of the fact that we're moving even closer to Xi architecturally, I can already anticipate the criticism that what we're doing here is redundant and we should just contribute to Xi. Why bother with this?

First of all, I'm definitely not opposed to some sort of partnership with Xi. I have tremendous respect for the technical work they've done and really *like* Raph personally. That said, it's complicated. We have our own ideas for where we want to take this project and what it means to GitHub. At the moment, the only way I see to guarantee we can achieve those ideas is to have enough creative control to iterate and follow our own path. Xi has been around for a while now, and it would be rude and presumptuous to roll in and start telling them how they should run their project. If we want to be active participants in our own destiny without telling other people what to do, it seems like we need to take our own path, at least for now.

We would like to learn as much as we can from the Xi team, and we'd be happy if we could provide value to them as well. It seems like sharing ideas would be a necessary prerequisite to sharing code. If the work we're doing proves valuable enough for the Xi team to want to invite us in as partners, then that would be great, but we're really still experimenting here with how best to achieve our particular goals. It's not clear that we have the same priorities as the Xi team, and it's not obvious that our priorities are sufficiently compatible for us share a codebase. And that's okay. There's room for more than one editor in the world.

If we do end up having enough social and technical alignment to share code, then that would obviously be great for us, because we'd be working with some incredibly smart people. I want to be open minded, but I also want the freedom to create based on our own ideas without telling anyone else what to do. So it's complicated. It's not crystal clear what the right path is, but we're doing our best to make the best decisions with the experience and wisdom we've managed to acquire to this point.


================================================
FILE: docs/updates/2018_03_19.md
================================================
# Update for March 19, 2018

## Contributions

We have a couple of PRs pending ([#36](https://github.com/atom/xray/pull/36) and [#34](https://github.com/atom/xray/pull/34)), but we're holding off on merging anything until we complete some major architectural changes. Sorry for the delay [@LucaT1](https://github.com/LucaT1) and [@breezykermo](https://github.com/breezykermo).

## Selections optimizations

[I merged a PR](https://github.com/atom/xray/pull/45) from [@as-cii](https://github.com/as-cii) that optimized our initial implementation of selections. While we still think there is room for more optimization, we're pretty happy with our early results. On Antonio's machine, he's moving 1k selections in a document with 10k edits in under 2ms. Based on some hacky experimentation to avoid allocations, we think we can make that even faster. At some point, with some number of selections, we're going to end up blowing our frame budget, but we think maintaining it into the thousands of selections ought to be acceptable.

## Significant progress switching to a client/server architecture

[@as-cii](https://github.com/as-cii), [@maxbrunsfeld](https://github.com/maxbrunsfeld) and I have made decent progress on a PR to switch Xray to the client/server architecture I [discussed last week](./2018_03_12.md#big-architectural-changes-incoming).

We're implementing an event-driven server using [Tokio](https://tokio.rs/), and have what seems like a viable approach for relaying data between the server and the window that will leave the door open to packages implementing custom views that slot in cleanly next to built-in features.

Check out [#46](https://github.com/atom/xray/pull/46) for details. I've also written [a fairly detailed document](https://github.com/atom/xray/blob/198e3bdf3c284679a5520923b0e27b079cc23377/docs/architecture/001_client_server_protocol.md) explaining our architecture and the protocol that will become a permanent part of Xray's documentation once this PR is merged.


================================================
FILE: docs/updates/2018_03_26.md
================================================
# Update for March 26, 2018

## Contributions

[@matthewwithanm](https://github.com/matthewwithanm) of Facebook's Nuclide team helped us improve our React game by [avoiding the use of deprecated string refs](https://github.com/atom/xray/pull/50) and [avoiding the use of component `state` for data that is unrelated to rendering](https://github.com/atom/xray/pull/51). Thanks Matthew!

## The switch to a client/server architecture is complete

We merged [#46](https://github.com/atom/xray/pull/46) last week, completing our switch to a client/server architecture. JavaScript in Xray's user interface now communicates with the Rust core over a domain socket rather than via a native V8 extension, which dramatically simplifies our build process. We connect to the server over a domain socket, which unfortunately means that Xray doesn't work on Windows for now due to the unavailability of domain sockets in the OS. If anyone is interested in adding support for named pipes on Windows to `xray_server`, we'd gladly collaborate on a pull request. If you've tried to build Xray and ran into trouble, now would be a good time to try again on non-Windows platforms after [carefully reading our build instructions](../../CONTRIBUTING.md#building).

## Updated roadmap

We've adjusted our roadmap a bit to prioritize collaborative editing rather than focusing on producing WebAssembly-based editor build. A browser-compatible editor is still part of our long term plan and we're designing the system with that requirement in mind, but since we want all of Xray's features to support remote collaboration, it makes sense to get it into the architecture early.

## Fast file finding

Xray is currently hard-coded to open a single buffer containing the dev build of React, which isn't very useful. To fix that, [we're adding a file finder](https://github.com/atom/xray/pull/55) that can quickly filter all files in the project that match a given subsequence query.

To obtain good search performance, we're maintaining an in-memory replica of the names of all the files and directories in the project which we can brute-force scan on a background thread whenever the query changes. We represent this data as a simple tree which reflects the hierarchy of the file system. To ensure that we can respond to user input within our 50ms deadline for coarse-grained interactions, we really want to be able to run queries before we finish reading all of the entries from the file system. To enable that, we're designing our in-memory file tree to support concurrent reads and writes.

We spent a decent amount of time exploring different approaches that could enable this, and ultimately we decided to protect the entry vector for each directory with a fine-grained read/write lock. When [@as-cii](https://github.com/as-cii) first suggested this approach, I was worried that it would consume too much memory, but I then discovered the [parking_lot](https://github.com/Amanieu/parking_lot) crate, whose `RwLock` implementation only consumes a word of memory per instance.

The basic logic of searching will be in `xray_core` and is modeled as a `Future` to give us flexibility in how we schedule it. For `xray_server`, which runs as a standalone binary and has full threading capabilities, we can simply spawn the search on a thread pool. Until WebAssembly adds threading support, we can implement some kind of background scheduler that uses `requestIdleCallback` to break the work up into smaller chunks before yielding the thread.

Rust futures are based on a polling model, where the executor repeatedly calls `poll` on the future to drive it to completion. To support granular yielding in a single threaded environment, we really need to execute the minimal amount of work each time `poll` is called on our `fs::Search` future. To enable that, we maintain a stack within the future that tracks our current position within the tree. The stack keeps an `Arc` (atomic reference-counted) pointer to the entries of each directory, along with the current index into that list of entries. Since concurrent writers could insert entries that might invalidate these indices, we treat directory entries as clone-on-write if we detect they are referenced by more than one `Arc` pointer, via the `Arc::make_mut` method. Most of the time, writes should be able to freely mutate a directory's vector of entries, but if that write might interfere with an ongoing search, we clone the vector to avoid invalidating any active indices.

The work is still in progress, but we're hoping this design will enable a highly user responsive experience for file finding even in the presence of extremely large source directories. We'll report on our findings in the next update.

## Thoughts on key bindings and actions

We're optimistic that we can finish up a basic (but fast) file finding UX some time next week. After that, I think it's time to tackle key bindings. Atom's key binding implementation is insanely complex and jumps through some ridiculous hoops to support a long tail of different locales and features like overlapping multi-stroke bindings, binding to key-up events, etc. Eventually, we want Xray to support all of these features as well, but in the short term, we want to keep the implementation as simple as possible. We're going to start by targeting single-stroke bindings and avoid any gymnastics to workaround browser limitations in various international locales. We'll revisit these concerns after getting some more traction in other areas of the system.

Our strategy with Atom was to "embrace the web", which led us to associate key bindings with CSS selectors. This was a neat idea and served Atom reasonably well, since CSS selectors are a powerful tool for describing a specific context in the DOM. However, in the end I don't think the power was worth the complexity of full-blown CSS selectors. Their flexibility makes it extremely difficult to build a user interface for configuring bindings, and the complex rules for evaluating selector specificity can lead to a frustrating experience.

With Xray, I want a system for making key bindings context-sensitive that is flexible enough to support most reasonable use cases, but not so flexible that it becomes hard to reason about. My thoughts are still evolving on this, but I'm thinking about representing the context in which we interpret a key binding as a set of simple tokens called an "action context". A custom component can be used to refine this context for a subset of the view hierarchy by adding or removing tokens.

Let's use an example to explain how the system would work. This is going to be a bit contrived, but it's not totally unrealistic. Imagine you wanted to write a spell-checking extension that allowed the user to display a list of suggestions next to a misspelled word that could be navigated from the keyboard. It might look something like this:

```js
class SpellingSuggestions extends React.Component {
  render () {
    <ActionContext
      add={["SpellingSuggestions", "VerticalNav"]}
      remove={["Insert"]}>
      <Action type="NavUp"/>
      <Action type="NavDown"/>
      <Action type="Confirm"/>
      <div>...</div>
    </ActionContext>  
  }
}
```

In the example above, we declare a refinement to the action context via an `ActionContext` JSX tag at the root of the component, adding the `SpellingSuggestions` and `VerticalNav` tokens and removing `Insert`. We then declare three actions that this component handles via `Action` tags: `NavUp` and `NavDown`, and `Confirm`.

Normally in the editor, the up and down arrows would be bound to the `MoveCursorUp` and `MoveCursorDown` actions, which move the cursor. But when your menu is displaying, you want the arrow keys to select the next or previous item in the list instead. To enable that, the up and down arrow keys could be bound to `NavUp` and `NavDown` within the `VerticalNav` context. The left and right arrow keys would continue to move the cursor, and potentially dismiss the menu if you moved out of the misspelled word.

If you didn't like the menu hijacking your cursor movement, you could unbind the arrow keys in the `VerticalNav` context, or maybe leave the arrow keys bound but preserve the Emacs-style `ctrl-p` and `ctrl-n` bindings for cursor movement.

Users might also bind `j` and `k` to `NavUp` and `NavDown` in any context that is not `Insert`. The text editor would introduce `Insert` to the action context because it inserts text, but the spelling suggestions menu could temporarily override that by removing `Insert` from the context. So could a Vim extension in command mode.

This system is still pretty complex, but its semantics are much simpler than CSS selectors, and it seems like it could cover compositional scenarios like the one described above rather well. We could easily provide some kind of global registry of action context tokens that gives them a human-readable name and description, then use that in a user interface that makes it convenient for users to customize their bindings in specific contexts without opening a JSON file.


================================================
FILE: docs/updates/2018_04_02.md
================================================
# Update for April 2, 2018

## Contributions

[@chgibb](https://github.com/chgibb) helped us get an initial Travis build in [#48](https://github.com/atom/xray/pull/48). This partially addresses [our help-wanted issue](https://github.com/atom/xray/issues/22), but we're still going to leave it open since we want to run the minimal tests for a given change to mitigate one of the downsides of being a monorepo. Thanks to @chgibb for getting this started.

## Almost done with the file finder

Our main focus last week was finishing up the file finder feature that I also [discussed in the previous update](./2018_03_26.md#fast-file-finding). The last update was all about our approach to scanning the directory tree from the file system into an in-memory representation, and the approach we described remains pretty much unchanged. We plan to merge [the pull request](https://github.com/atom/xray/pull/55) early this week.

### Leveraging prior art

Last week was all about using that in-memory representation to return search results based on a "fuzzy" search query. After an initial attempt that yielded decent performance but poor ranking of the search results, we decided to investigate existing solutions. We tried two command-line fuzzy finders, [`fzy`](https://github.com/jhawthorn/fzy) (written in C) and [`fzf`](https://github.com/junegunn/fzf) (written in Go) on the Electron repository, which contains over 500,000 files when `.gitignore` is disabled.

Both tools yielded excellent performance and high quality results, and since the [core matching algorithm](https://github.com/jhawthorn/fzy/blob/47609dbf73789bc28289576a12177965c04ef49b/src/match.c#L70) behind `fzy` was reasonably straightforward to read and understand, we decided to port it to Rust. You can [read more about the algorithm](https://github.com/jhawthorn/fzy/blob/master/ALGORITHM.md) in the `fzy` repository, but at a high-level, their solution is based on dynamic programming and determines the optimal match positions for a given substring by populating a matrix with cascading values. We copied their basic approach almost exactly, but we also enhanced it a bit to make use of the existing tree structure to recycle computation for common path prefixes.

### Matching and scoring

Xray matches paths in two phases. First, [we scan the tree to determine which paths match the query](https://github.com/atom/xray/blob/3c25fc7a7328b0ce1f6746990689e0f80bca3009/xray_core/src/project.rs#L93), populating a hash map to mark which file system entries either match the query or contain matches to the query. Simply matching the query only requires us to perform linear character comparison and is fairly cheap to perform, and this allows us to constrain the search space for the next step. Once we determine matches, [we then walk the tree to associate each matching path with a score](https://github.com/atom/xray/blob/3c25fc7a7328b0ce1f6746990689e0f80bca3009/xray_core/src/project.rs#L154). Scoring is O(N*M), where N is the length of the query and M is the length of the path. Luckily, longer queries tend to match fewer paths, which means when it is most expensive to compute scores, we usually end up needing to compute fewer of them.

### Results

Overall, we're happy with the results. The quality of the matches is extremely high thanks to the work [@jhawthorn](https://github.com/jhawthorn) put into tuning the scoring criteria. Since ranking matches is somewhat subjective, basing our results on an existing, fairly mature solution gives us a lot more confidence in the quality of the results. The performance is also pretty decent. Searching for `init` in the 151,201 files of the [`blink`](https://chromium.googlesource.com/chromium/blink/+/master) repository yields results in ~120ms on my machine. Searching for `init.py`, which is a more selective query, drops that to ~16ms.

### Future improvements

These early results are good, but we think there's room for improvement. First, we're still matching on a single thread, and it seems like we might be able to use [Rayon](https://github.com/rayon-rs/rayon) to parallelize the matching over multiple CPU cores. We could also do a better job reporting progress. 20ms into the query we could check if we are more than 20% complete with ranking, and if we aren't we could display some sort of subtle progress indicator. That could help the search feel *responsive* even if it takes 100+ms to return results. That said, we're going to call this good for now and move on to other areas. The file finder *feels* fast and fluid now, even for big repositories, and we think we have a solid foundation in place for future improvements.

## Other improvements

Since we're still fairly early in development, we're allowing branches to get longer and heavier than we might in a more established project. Folded into the file finder branch are a few smaller improvements that made sense to add along the way.

### Window and view API refinements

We display the file finder as a modal in the workspace, and when the user selects a file or cancels the modal, we need to take action in the workspace. After pondering a couple of approaches, we ended up deciding to use a fairly traditional delegate pattern here, where the `WorkspaceView` implements the `FileFinderViewDelegate` trait and passes a weak reference of itself to the `FileFinderView`.

Trouble is, how does the `WorkspaceView` obtain a weak reference to itself? Since the `Window` wraps each view in an `Rc<RefCell>`, we ended up deciding that it would be convenient for the window to [pass each view a `WeakViewHandle` to itself](https://github.com/atom/xray/blob/3c25fc7a7328b0ce1f6746990689e0f80bca3009/xray_core/src/window.rs#L116) in the view's `will_mount` hook. Many views can simply ignore this parameter, but if views need to perform delegation they can safely store and clone it without worrying about leaking memory, enabling them to hand itself as a delegate of child views. This is how [we connect](https://github.com/atom/xray/blob/3c25fc7a7328b0ce1f6746990689e0f80bca3009/xray_core/src/workspace.rs#L48) actions dispatched on the `FileFinderView` to state changes in the workspace.

### Focus API

We also needed a way to focus the file finder when it displays, then focus the newly opened editor after a file is selected. We decided to implement this on the server side via the new `ViewHandle::focus` method. Whenever this method is called, it assigns the `focused` field on the `Window` to the focused view's id. This gets relayed to the client, which calls the `focus` method on the corresponding React component.

For now, we aren't interested in replicating the focus state to the server. Server-side code can request that a view be focused, but it can't ask which view is currently focused. This is a decision we can revisit later, but focus is a very weird piece of global state that references individual DOM nodes, so it doesn't seem worth the complexity of attempting to represent it outside of the browser environment. This means that the modal panel will still need to have a bit of custom focus handling logic in order to restore focus to the previous element when cancelled, but so far this seems manageable.

### CLI improvements

We've also changed the structure of the CLI's relationship with the server and Electron slightly. Previously, when we spawned Electron, we could ask it to relay a message to the server via the `XRAY_INITIAL_MESSAGE` environment variable. Now, the CLI waits for the Electron app to emit `Listening\n` on `stdout`, then attempts to connect to the server itself to send the initial message.

We made this change to deal with error handling. The server may need to report an error message to the CLI over the socket, and this was going to be complicated to achieve with the previous approach of delegating the initial message send to Electron.

Waiting for Electron to tell us the server has started may introduce some latency, which is why we initially preferred the delegation approach, but we'll need to actually measure this before the additional complexity is warranted in light of the need to receive a response from the server.

## The week ahead

We hope to merge the file finder PR. All that's left is some basic styling and iteration on focus handling.

After that, we plan to start working on shared headless workspaces. The hardest part is enabling concurrent text editing, but that's pretty much solved by our use of a CRDT as Xray's core text-storage structure. However, there's still plenty of complexity remaining in terms of how we actually connect buffer replicas together and structure the client/server interaction.

We plan to explore [Cap'n Proto RPC](https://capnproto.org/rpc.html), which seems to have an actively-maintained [Rust implementation](https://github.com/capnproto/capnproto-rust). None of us has ever used it, so we'll need to see how the reality matches up to its promises, but on initial investigation it looks like it could be a good fit for Xray's needs.

Cap'n Proto offers a compact yet evolvable binary representation for messages, and the RPC system seems like it makes it easy to expose any object over the network in a [secure way](https://capnproto.org/rpc.html#security) and [efficiently call its methods](https://capnproto.org/rpc.html#time-travel-promise-pipelining). As long as they're well-implemented, these features seem sufficiently general to be a foundation for network interaction between Xray instances.

At this point, Xray is still too young to be usable. But we're trying to ruthlessly prioritize and zero in on the highest value and highest risk aspects of the system as soon as possible. It's unfortunate that Xray doesn't build on Windows right now, but there's honestly not that much to see or use anyway. If you're a Windows user and you're interested in helping out, getting a named-pipes- or TCP-based connectivity solution in place on Windows would be a great place to start.


================================================
FILE: docs/updates/2018_04_09.md
================================================
# Update for April 9, 2018

## Shared workspaces

We spent the entire week [laying down the foundations that will enable shared workspaces](https://github.com/atom/xray/pull/61). What are shared workspaces? The basic idea is that you'll be able to start a headless Xray instance on a remote machine, then have multiple developers connect and co-inhabit that workspace from their local machines.

The fact that our buffers are CRDTs makes concurrent buffer editing relatively straightforward to implement, but we still need a solution for synchronizing state between peers and performing requests and response. After experimenting a bit with Cap'N Proto RPC and feeling a bit overwhelmed by the generated code, we decided to explore what a custom solution might look like.

We're not quite done with the implementation, but after a lot of thinking and a bit of wheel-spinning, we have a reasonably solid design for a capabilities-based RPC system that will be a good fit for our use case. I've written up [a much deeper description](https://github.com/atom/xray/blob/9a1a02b7b608225a4c60fa364a1d60c1ef5f59c2/docs/architecture/002_shared_workspaces.md) that will become part of Xray's permanent documentation. Here's a *huge* diagram to get you interested:

![RPC Diagram](../images/rpc.png)

## The week ahead

We hope to finish an initial take on the RPC system next week, then start using it to build out a basic demo of shared workspaces. Our goal is to make it possible to find and open paths on the client and support concurrent editing by multiple clients. That may spill into the following week, when I'll be traveling Amsterdam for some in-person full-throttle coding with [@as-cii](https://github.com/as-cii).


================================================
FILE: docs/updates/2018_04_16.md
================================================
# Update for April 16, 2018

## Contributions

[@rleungx](https://github.com/rleungx) [set up a basic benchmarking framework](https://github.com/atom/xray/pull/62) that uses [Criterion](https://github.com/japaric/criterion.rs).

## Progress on shared workspaces

By the middle of last week, we had a first iteration of the RPC system that we were happy with, and started using it to build out shared workspaces. To do that, we're adding replication to Xray's model objects. The goal is to be able to use model objects without worrying about whether or not they are remote or local.

We're converging on a design where most model objects are represented by a trait, with local and remote concrete implementations of this trait. For example, the project model has a `Project` trait along with `LocalProject` and `RemoteProject` implementations. We also have an `rpc::server::Service` implementation that has a shared reference to a `LocalProject` and exposes it to a remote client. On the client side, the `RemoteProject` owns a `rpc::client::Service` object. When you call a method like `open_buffer` on the client side, it's translated into a network request to a service on the remote peer, which translates the request to a method call on the corresponding `LocalProject`.

We have unit tests passing for replication of file system trees and projects, along with the initial state for buffers. We still need to replicate buffer edits. We also have some work to do to refine our treatment of ownership for services on the server side. We think the best approach might be to enable both the client and the server to retain services. So if the server wants to keep a service alive and return it across multiple requests or updates, it can store off a handle to the service. Or it can drop the handle, in which case the client can take ownership over the service. Once the client drops, we'll communicate this fact across the wire and decrement the service's reference count. It's essentially an `Rc` transmitted over the network. We'll see how it goes.

## Syntax awareness

This week, [@maxbrunsfeld](https://github.com/maxbrunsfeld) will be diving in on integrating the [Tree-sitter](https://github.com/tree-sitter/tree-sitter) incremental parsing system into Xray. The first step involves some adjustments to the runtime to enable syntax trees to be fully persistent and sharable across threads. Xray's buffers already support this kind of usage, so including syntax trees will enable lots of interesting computations to be pushed into the background.

## Heads-down in Amsterdam

[@as-cii](https://github.com/as-cii) and I are meeting up in Amsterdam this week to write as much code as possible together in person. To that end, I'm going to keep this update short so we can get to work.


================================================
FILE: docs/updates/2018_04_23.md
================================================
# Update for April 23, 2018

## An initial implementation of shared workspaces is complete

Last week we [completed the initial milestone for shared workspaces](https://github.com/atom/xray/pull/61), which allows you to connect to a remote Xray instance over TCP and open one of its workspaces in a new window. You can then use the file-finder to locate and open any file in the remote project and collaboratively edit buffers.

There is obviously a ton more work to do until we can call our implementation of shared workspaces "done". Xray isn't even really usable right now for even basic text editing due to a long tail of missing features. Regardless, we think it's really important to have this infrastructure in place early. From here on out, every feature we build will be designed to support remote collaboration, and the foundation we've laid over the last two weeks will make that possible. We're pretty excited about the potential RPC system we've built. By combining remote procedure calls with eagerly replicated state and the judicious use of conflict-free replicated data types, we think we can abstract away the physical boundaries that separate individual machines and developers.

## Browser compatibility

The [four pillars of Xray](../../README.md#foundational-priorities) are performance, real-time collaboration, browser compatibility, and extensibility. 8 weeks into focused development, we're feeling confident that Xray's architecture can meet our desired performance goals, and we've validated an approach that will bake collaboration into the heart of the system. Before burning down the long list of features that make up a usable text editor, we want to take some time to put the last two pillars in place by getting Xray working in a browser and laying the foundation for extensibility. By taking care of all four of these high-level concerns early, we'll ensure that they're supported as we build out the remainder of the system.

To that end, we're now turning our attention to browser compatibility. We've actually been designing Xray with this goal in mind from the beginning. Today, Xray comprises two major components: `xray_server`, which contains the core application logic, and `xray_electron`, which presents the user interface and communicates with `xray_server` over a local socket. Now we need to create versions of these two components that run inside of a web browser.

As a browser-based counterpart of the `xray_server` executable, we're creating `xray_wasm`, which will be compiled to WebAssembly and run in a web worker. `xray_wasm` will share the majority of its implementation with `xray_server` via a dependency on the platform-agnostic `xray_core` crate. `xray_core` abstracts its communication with the outside world in terms of abstract traits defined by the Rust `futures` crate. Methods for connecting to remote peers and the user interface accept and return `Stream`s of binary buffers, and the application also expects to be passed `Executor` instances that can schedule futures to be executed in the foreground or background.

In the browser, we'll move data via message channels and web sockets rather than using domain sockets and TCP, but these are just transport layers and are easy to abstract in terms of `Stream`s and `Sink`s so they can be passed into the platform-agnostic code. Similarly, we'll integrate with the browser's event loop by writing a custom `Executor` that uses the `Promise` API or `requestIdleCallback` to defer computation.

We're using the `wasm-bindgen` crate to interoperate between Rust and JavaScript, and last Friday we managed to get asynchronous bi-directional communication working between Rust and JavaScript. This week, we plan to extract as much UI code as possible from `xray_electron` into a common library called `xray_web`. We'll then create `xray_browser`, which will package everything together into a browser-deployable bundle that runs the core application logic in a web worker and connects it to the UI running on a web page.

Since browsers strongly sandbox interaction with the underlying machine, we will only support interactions with remote shared workspaces when Xray is running in a browser. We plan to add WebSockets support to Xray server so that it can accept connections from browser-based clients. We'll also add an `--http` option that exposes a simple web server from `xray_server` that serves a browser-based UI to clients. This will obviously require a security scheme to be useful in a production setting, but it seems like a good way to develop the browser-based user experience. A simple password-list based security scheme would also be pretty quick to add.


================================================
FILE: docs/updates/2018_04_30.md
================================================
# Update for April 30, 2018

## Xray now runs in a browser

Last week, we merged [#67](https://github.com/atom/xray/pull/67), which allows Xray to be run inside of a web browser. The design is different in a couple of details from what I anticipated in last week's update, but the big picture is pretty much what we expected. The main difference is that for now, we decided not to bake HTTP and WebSockets support directly into `xray_server`, but instead place them in [a simple development server](https://github.com/atom/xray/blob/92f6c1959f843059738caff889df0843836cc006/xray_browser/script/server) which is written in Node and proxies WebSocket connections to `xray_server`'s normal TCP-based connection listener. This made it easy to integrate with middleware for WebPack that recompiles our JS bundle during development. Long-term, we'd still like to host web clients directly from `xray_server`, but we want to bundle the static web assets directly into the binary so that `xray_server` can continue to work as a standalone executable. This should definitely be possible, but it doesn't feel important to address it now.

## Demo this week

We plan to show off Xray's progress to some colleagues here at GitHub later this week, so to that end, we'll focus some of this week on smaller details that, while not fundamentally advancing architectural concerns, will end up making for a better demo.

By the end of this week, we should be rendering the cursors and selections of remote collaborators. We also plan to add a discussion panel to the Xray workspace where collaborators can have a text-based conversation that is linked to their code.

Once the demo is behind us, we plan to take a few days to burn down any technical debt we have accrued in the 10 weeks we've been actively developing the project. The biggest thing on our agenda is updating to [futures 0.2](http://aturon.github.io/2018/02/27/futures-0-2-RC/) and the [latest version of tokio](https://tokio.rs/blog/2018-03-tokio-runtime/). We also plan to take a look at our build and see if we can make our CI turnaround faster.


================================================
FILE: docs/updates/2018_05_07.md
================================================
# Update for May 7, 2018

## Contributions

[@yajamon](https://github.com/yajamon) contributed [a fix for an oversight in our build script](https://github.com/atom/xray/pull/78) where we were specifying `+nightly` even though our repository is associated with a `rust-toolchain` file. Thanks!

## First internal demo is complete

As I mentioned in the last update, we focused last week on preparing for an internal demo that presented at least a tiny slice of the Xray vision in a more tangible, interactive form. We spun up a headless Xray server as a digital ocean droplet and showed off remote shared workspaces, collaborative editing, and conversations tied to the code. We also put together a few slides demonstrating Xray's performance for various tasks such as fuzzy file-finding, moving large numbers of cursors, and scrolling. The response was really positive, and we've elected to continue the experiment into the next quarter. [@as-cii](https://github.com/as-cii) and [I](https://github.com/nathansobo) will continue to focus on Xray in the coming months, and we'll get a bit of support from [@maxbrunsfeld](https://github.com/maxbrunsfeld) in order to integrate [tree-sitter](https://github.com/tree-sitter/tree-sitter) as the basis of Xray's syntax awareness.

## Into the unknown with CRDTs

As [I discussed in the first update](./2018_03_05.md#anchors-and-selections), Using CRDTs in Xray's native buffer implementation allows us to create *anchors*, which are stable references to positions within a text file that maintain their logical position even after the buffer is subsequently edited. For our discussions feature, we use anchors to link each message to the range of the buffer that was selected at the time the message was sent. This allows you to select a code fragment and make a comment, then allow other participants to click on the message at some later time to jump to the code you had selected when you sent the message. For now, Xray only maintains all of this state in memory. The discussion history is lost as soon as you kill the process, and we deliberately avoid dropping buffers once they are open in order to preserve the validity of anchors. This is obviously not going to work, and to fix it, we need to figure out how to persist each buffer's operation history.

If we assume that buffers are never renamed and that history only ever marches forward, this is pretty easy. But the possibility of renames and interactions with Git (or other version control systems) make it interesting. We want to track a file's identity across renames and ensure that we load the appropriate history when the user switches branches, and these concerns have a lot of overlap with some other ideas we've been pondering that can loosely be described as "real-time version control". With a proof-of-concept for shared workspaces behind us, we think it's time to explore them.

Currently, we represent buffers as CRDTs. We're interested in what happens if we take that further and treat the entire *repository* as a single unified CRDT data structure that is persisted to disk. Ideally, assuming Xray is used for every edit, we will be able to maintain a keystroke-level history of every edit to every file all the way back to the moment that each file was created, sort of like an infinite conflict-free undo history. But of course, there will be many cases where files change occur outside of Xray, so we'll need to gracefully handle those situations as well. We've decided to spend the next couple weeks exploring this. We'll probably spend most of our time clarifying our thoughts in writing at first before transitioning to coding. It's unclear exactly how much gold is at the end of this particular rainbow, but it seems worth a look.

## Strike out with futures 0.2

On Friday, we spent an hour and a half upgrading `xray_core` to `futures` 0.2, only to discover that Tokio doesn't yet support that version 🙈. Luckily, it wasn't that much time wasted, but we did feel somewhat foolish for assuming that Tokio worked with it without checking first.

## Optimizations

[@as-cii](https://github.com/as-cii) has been picking some low-hanging optimization fruit related to selections and editing. The [first](https://github.com/atom/xray/pull/79) is related to adding selections above and below the cursor. He's also been looking at [batching tree manipulation](https://github.com/atom/xray/tree/optimize-edit) when editing with multiple cursors, which is still in progress and is not yet associated with a PR.


================================================
FILE: docs/updates/2018_05_14.md
================================================
# Update for May 14, 2018

## More optimizations

Last week we spent a couple of days speeding up multi-cursor editing. Specifically, we wanted to take advantage of the batched nature of this operation and edit the buffer's CRDT in a single pass, as opposed to performing a splice for each range. Please, take a look at [#82](https://github.com/atom/xray/pull/82) for all the details. 

There is still some work to do in that area to deliver a smooth experience when editing with thousands of cursors, but we are planning to get back to it once we have fleshed out more features.

## Thoughts on further applications of CRDTs

After demoing Xray to our colleagues, we got a lot of interest in how Xray's CRDT-based approach to buffers might apply to the problem of versioning generally, so we took some time to explore it last week. We were intrigued by the idea of a CRDT-based analog to Git, a kind of operation-oriented version control system that allowed for real-time synchronization among several replicas of the same working tree and persistence of all operations. After spinning our wheels quite a bit, we've concluded that we really need to get clear on the specific problems we might like to solve. They are as follows:

* Replay: We'd like to allow developers to record a collaboration session and cross-reference their keystrokes to audio, so that it could be replayed later. Assuming people were willing to opt into this, it could provide deep insights into the thought processes behind a given piece of code to future developers. This use case is really all about persisting the operations, and has nothing to do with replicating the entire file tree.

* Permalinks: Today we have anchors, which automatically track a logical position in a buffer even if in the presence of concurrent and subsequent edits, but these anchors are only valid for the lifetime of the buffer in memory. We'd like to be able to create an anchor that can always be mapped to a logical position at arbitrary points in the future, even thousands of commits later. Again, this has nothing to do with full replication. It's really about *indexing* the operations we persist and tracking the movement of files over time so that we can always efficiently retrieve a logical position for an anchor.

* Streaming persistence and code broadcast: Today, code lives on your local machine until you save it, commit it, and push it to the cloud. We want to persist your edit history as it is typed and optionally stream it into the cloud. If your computer spontaneously combusts, your up-to-the-minute edit history is still saved on the server. If you elect for your edits to be public, colleagues or community members could watch your edit stream in real time. This would require full replication if you wanted to allow another party to make *edits* to the working tree. If the server is just storing your operations, there's really no need to deal with concurrency. It *might* be cool if someone could come along and edit the server's replica of the work tree and have their edits automatically appear in your replica, but is that actually a good user experience? Real-time collaboration requires tight coordination, so it might be jarring to receive edits from someone you didn't actively invite to your workspace.

* File-system mirroring for third-party editors: We'd like to allow other editors to use Xray in headless mode as a collaboration engine. In this use case, we'd need to relay edit operations through Xray via specific APIs, but it might be helpful if Xray could mirror the state of a remote project to the local file system. That way, an exiting editor could use its ordinary mechanisms for dealing with local files to interact with the remote workspace, and wouldn't need to perform file system interactions over RPC, which would simplify integration.

I wanted to think through the design implications of these various features early to determine whether any of them had an impact on Xray's core architecture, and after a lot of thinking, my conclusion is that it should be okay to defer these features for now. I had envisioned a single unified design that elegantly addressed all of these features in a single replicated structure, but now we think that that cost of building such a structure probably outweighs its benefits.

For now, we've decided to defer these concerns to until the point that replay, permalinks, or streaming persistence are actually the next most important feature we want to add. Our instinct is that when that time comes, we'll be able to address these features in an additive fashion, and that it doesn't make sense to invest in adding support for them today.

In retrospect, last week was a bit of a distraction. I've done more up-front design thinking for Xray than I ever have for any other project, and it's worked out pretty well overall. But after last week, I think we're approaching diminishing returns for up-front architectural design. We've validated that the current design can be performant and collaborative, and it's seeming like we've struck a nice balance between simplicity and power. Now it's time to return to a more incremental strategy and continually focus on the next-most-important feature until we have a useful editor.

## The path forward

This week, we'll turn our focus to implementing save as well a simple key bindings system, which [I wrote about in a previous update](2018_03_26.md#thoughts-on-key-bindings-and-actions). We also plan to clarify our short term roadmap, and we'll post an update about that next week.


================================================
FILE: docs/updates/2018_05_28.md
================================================
# Update for May 28, 2018

## Staying the course with CRDT-based version control

In the last update, I said that we were abandoning our efforts to apply CRDTs to the entire repository, citing lack of clarity on what we were actually trying to achieve. However, after more conversations with colleagues, we've decided to proceed with that effort after all. After a lot more thinking and writing, we finally got enough clarity on our direction to start writing code last week.

We still plan to continue developing Xray as a text editor, but we're adding a new top-level module to the repository called Memo, which is essentially a CRDT-based version control system that interoperates with Git. Xray will pull in Memo as a library and build directly on top of its primitives, but we also plan to make Memo available as a standalone executable in the future to support integration with other editors.

Our plan is for Memo to complement Git with real-time capabilities. Like Git, Memo will support branches to track parallel streams of development, but in Memo, all replicas of a given branch will be synchronized in real-time without conflicts. For example, if you and a collaborator check out the same Memo branch, you'll be able to move a file while someones else is editing that file, and the state of the file tree will cleanly converge.

Today, Git serves as a bridge between your local development environment and the cloud. When you push commits to GitHub, you're not only ensuring that your changes are safely persisted and shared with your teammates, but you're also potentially kicking off processes on one or more cloud-based services to run tests, perform analysis, or deploy to production. We want to make that feedback loop tighter, allowing you to share your changes with teammates and cloud-based services as you actively write code.

With Memo, as you're editing, a CI provider like Travis could run tests across a cluster of machines and give you feedback about your changes immediately. A source code analysis service like Code Climate could literally become an extension of your IDE, giving you feedback long before you commit.

Like Git, we also intend to persist each branch's history to a database, but your changes will be continuously persisted on every keystroke rather than only when you commit. After the fact, you'll be able to replay edits and identify specific points in a branch's evolution via a version vector. When we detect commits to the underlying Git repository, we'll automatically persist a snapshot of the current state of the Memo repository and map the commit SHA to a version vector. When a commit only contains a subset of the outstanding changes, we'll need a more complex representation than a pure version vector in order to account for the exact contents of the commit, since a version vector can only identify the state of the repository at a specific point in time.

Last week, after getting clear on our goals, we started on a new tree implementation that we'll use to index the history of changes to the file system and text files. It's based heavily on the tree that we already use within Xray to represent the buffer CRDT, but we're modifying it to support persistence of individual nodes in an external database. This will allow us to index the entire operational history of files without needing to load that entire history into memory during active editing. Once we complete the initial implementation of this B-tree, we'll use it to build out a CRDT representing the state of the file system.

## More progress on the editor

While I've been focused on getting clarity in terms of version control, [@as-cii](https://github.com/as-cii) has continued to make progress on Xray itself. Last week he merged [a PR that adds support for horizontal scrolling](https://github.com/atom/xray/pull/90) the editor, which was a bit more challenging than it might sound.

To support horizontal scrolling, we need to know the width of the editor's content, which involves efficient tracking and measurement of the longest line. Previously, we maintained a vector of newline offsets as part of each chunk of inserted text to support efficient translation between 1-D and 2-D coordinates which we implemented by binary searching this vector. Antonio replaced this representation with a static binary tree, which is still stored inside a vector for efficiency. With the binary tree, we maintain the same offset information that was formerly available in the flat vector, but we also index maximal row lengths, which gives us the ability to request the longest row in an arbitrary region of the text in logarithmic time.

I'll be out next week on vacation, so Antonio plans to focus primarily on more editor features until I'm back on Monday, June 4th. He'll start with rendering a gutter and line numbers, which he already got started last week. In light of my absence, there's a good chance we could go another 2 weeks before the next update. Thanks for your patience.


================================================
FILE: docs/updates/2018_07_10.md
================================================
# Update for July 10, 2018

It's been a while since the last update, and I apologize for that. Our strategic direction has felt less clear to me over the past few weeks, and that lack of clarity combined with some difficulty in my personal life overcame my motivation to post for a while. I just wanted to turn inward and write code in relative isolation. Things are clearer and I'm feeling better, and I'd like to resume posting updates on a weekly basis and ask your forgiveness for the gap in communication.

## The emergence of Eon

When we demonstrated Xray for GitHub leadership in May, there was definitely interest in Xray's potential as a high-performance collaborative text editor that runs on the desktop or in the browser, but there was way *more* excitement about CRDTs and their potential to impact version control. At first, this feedback caused some cognitive dissonance for me. After working so hard on Xray, it wasn't easy to hear that what I considered to be an implementation detail was the most exciting aspect of what we had built. But the more I thought about it, the more intrigued I became with the application of CRDTs to version control. The idea had been floating around in my mind since early in the development of Teletype, but now I felt encouraged to take the idea more seriously.

After a bit of indecision, we decided to dive in. We've now shifted our focus to a new project called Eon, which enables real-time, fine-grained version control. Long term, we see Eon and Xray as two components of the same overall project. Eon will be an editor-agnostic datastore for fine-grained edit history that enables real-time synchronization. It will be like Git, but it will persist and synchronize changes at the granularity of individual keystrokes. We envision Xray as Eon's native user interface and the best showcase of its capabilities. One example is the idea of "layers", which are like commits that can be freely edited at any time.

Git never would have taken off if it had been trapped inside a particular editor, and so if we really want to maximize the utility of what we're building, it makes sense to be editor-agnostic at the core. That's why we've decided to focus on delivering Eon as a standalone project. It may look like we have stopped working on Xray, but since Xray will ultimately build on top of Eon, the spirit of the overall project continues.

Since I was presenting Eon at Qcon NYC, we briefly decided to pull out Eon into a separate repository, but then we decided that this was actually a bad idea. For now, we will [continue to develop Eon within the Xray mono-repo](https://github.com/atom/xray/tree/eon/eon) in order to keep the community and development focused in a single location.

## Progress on Eon

Previously, Xray's allowed you to invite guests into your workspace, but it was a centralized design. The workspace host owned all the files and serialized all guest requests to manipulate the file system. If the host dropped offline, the collaboration was over. With Eon, we're shooting for full decentralization. Multiple people can maintain a first-class replica of a given repository, just like Git.

To achieve that, over the past few weeks, we've been working on replicating the contents of the file system in addition to individual buffers. That means that if one person moves a directory while a collaborator adds a file inside of it, both parties will eventually converge to the same view of the world. It's proven to be a surprisingly complex problem.

We maintain a CRDT that represents the state of all the files and directories within the repository, but the only cross-platform way to detect file system changes is to scan the underlying directory structure and compare it to our in-memory representation. So far, we've focused only on directories, and we're caching inodes so we can detect when a directory is moved. We have yet to deal with files, which add the possibility of multiple hard links to the same file, but we're planning for them in our design. We also still need to deal with the fact that the file system might change in the middle of a scan, which might cause us to encounter a file or directory multiple times.

Once we detect a local change, we update the local index and create an operation to broadcast to other replicas. We've settled on a design in which each file or directory is assigned a unique identifier and associated with one or more *parent references*, which describe where that file is located in the tree. Directories can only have one parent reference since they cannot be hard linked, but files can have multiple. Additionally, directories are associated with *child references*, each of which has a name and corresponds to a parent reference elsewhere in the tree.

Each parent and child reference is a simple CRDT called a *last-writer wins register*. If a file is moved, we update its parent reference. If the same file is moved concurrently on another replica, we break the tie in a consistent way such that the file ends up in the same location in all replicas. Similarly, if two child references with the same name are created concurrently within a directory, only one of them will win across all replicas.

Inspired by the [Btrfs file system](https://en.wikipedia.org/wiki/Btrfs), we're storing the state of the file system in the same copy-on-write B-tree that we use to represent the contents of buffers. Our tree is implemented generically, enabling us to reuse the same code for different kinds of items. In the case of our file system representation, each item is a member of an enumeration, which allows us to store file metadata, parent references, and child references all within the same tree. Each parent and child reference is actually represented by multiple tree items that share a *reference id*. We enforce a total order between all items in the tree, honoring the leftmost item for any register as the current value of that register.

We've also enhanced Xray's original B-tree to allow nodes to be persisted in an external key-value store. This will allow us to maintain a history of how the file system has evolved, and we plan to allow interactions with our tree to filter out certain nodes based on a summary of their contents. This will enable us to avoid loading portions of the tree that contain items that aren't visible in a specific version of the tree, which will keep the memory footprint small for any single version while still allowing us to load past versions of the tree if desired.

In many ways arriving at our current approach was more challenging than coming up with the CRDT for text. We spent many days doing almost nothing but thinking and not writing much code, but now we're feeling pretty good about the design. It seems simple and almost obvious, which is probably a good sign that we're on the right track.


================================================
FILE: docs/updates/2018_07_16.md
================================================
# Update for July 16, 2018

## Breaking cycles

This week, we continued our focus on a fully replicated model of the file system. We're still focusing on directories only, driving our work with an integration test that randomly mutates multiple in-memory replicas of a file system tree and tests for convergence.

Mid-week, we hit a pretty major snag that we hadn't anticipated, but seems obvious in retrospect. Say you have two replicas of a tree that contains two subdirectories, `a` and `b`. At one replica, `a` is moved into `b`. Concurrently, on the other replica, `b` is moved into `a`. When we exchange operations, we end up with both directories in an orphaned cycle, with `a` referring to `b` as its parent and `b` referring to `a` as its parent, a state which we can't mirror to the underlying file system of either replica.

| Time | Replica 1 State | Replica 2 State     |
|:-----| :-------------- | :------------------ |
| 0    |  `a/` `b/`      | `a/` `b/`           |
| 1    |  `a/b/`         | `b/a/`              |
| 2    |  ???            | ???                 |

For any set of concurrent moves, it's possible to create a cycle, and you could potentially create *multiple* different cycles that share directories in certain diabolical cases. Left untreated, these cycles end up disconnecting both directories from the root of the tree. We still have the data in the CRDT, but it can't be accessed via the file system. We need to break them.

We spent the second half of this week thinking about every possible approach to breaking the cycles while also preserving convergence, and we ended up arriving at two major alternatives.

The first approach is to preserve the operations that create the cycle, but find a way to break the cycle when we interpret the operations. The trouble is that cycles are always created by concurrent operations, but because this is a CRDT, it's possible for concurrent operations to arrive in different orders at different replicas. This means a decision to break a cycle is order-dependent, and may need to be reevaluated upon the arrival of a new operation. Our best idea is to create an arbitrary ordering of all operations based on Lamport timestamps and replica ids. When a new operation is inserted anywhere other than the end of this sequence, we integrate it and then reinterpret all subsequent operations based on a state of the tree that accounts for the new operation. It's definitely doable and preserves the purity of the CRDT, but it also seems complex and potentially slow. It also means that we could end up synthetically breaking a cycle only to determine later that we don't need to break the cycle due to the arrival of a concurrent operation. This could cause seemingly unrelated directories to appear out of nowhere upon the arrival of a concurrent operation, which could be pretty confusing depending on the integration delay. We'd like Eon to generalize to async use cases in addition to real-time, and these "phantom directories" seemed like a real negative for usability.

The second approach, which we've decided to go with, is sort of a principled hack. Whenever we interpret a move at a given replica that introduces a cycle, we look at every move operation that contributed to the cycle and synthesize a new operation that reverts the operation with the highest Lamport timestamp. We then broadcast this new operation to other participants. Depending on the order that various concurrent operations arrive at different replicas, we may end up reverting the same move redundantly or reverting multiple moves that participate in different variations of the same cycle. We considered this approach within the first hour of our discovery of the issue, but initially discarded it because it seemed to violate the spirit of CRDTs. It seems weird that integrating an operation should require us to generate a new operation in order to put the tree in a valid state. But after fully envisioning the complexity of the pure alternative, synthesizing operations seemed a lot more appealing. Breaking cycles via operations means that once a replica observes the effects of a given cycle being broken, they'll never see it "unbroken" due to the arrival of a concurrent operation. It also completely avoids the issue of totally ordering operations and reevaluating subsequent operations every time an operation arrives.

One consequence of either approach is that there could be certain combinations of operations that lead to a cycle that we never detect and break. That means that certain version vectors might yield tree states containing cycles and constrains the set of version vectors we should consider valid. This isn't a huge deal, because even without cycles, the constraints of causality already limit us to a subset of all possible version vectors if we want a valid interpretation of the tree. For example: If replica 0 creates a directory at sequence number 50 and replica 1 adds a subdirectory to it at sequence number 10, the state vector `{0: 20, 1: 10}` would contain a directory whose parent doesn't exist. If we limit ourselves to version vectors corresponding to actual states observed on a replica, we will have no problems.

## Homogenous trees

As I discussed in the previous update, we currently represent the state of the file tree inside a B-tree with heterogenous elements. Each tree item is either metadata, a child reference, or a parent reference. Now I'm realizing this is probably wrong. If we separated metadata, parent references, and child references into their own homogenous trees, we could probably simplify our code, reduce memory usage, and perform way pattern matching on the various enumeration variants. We plan to try separating the trees this week.

## Conclusion

For whoever is reading these updates, thanks for your interest. We're always interested in thoughts and feedback. Feel free to comment on this update's PR if there's anything you'd like to communicate.


================================================
FILE: docs/updates/2018_07_23.md
================================================
# Update for July 23, 2018

## Contributions

[@MoritzKn](https://github.com/MoritzKn) [fixed a bug](https://github.com/atom/xray/pull/115) where we were incorrectly calculating the position to place the cursor when inserting strings containing multibyte characters. Thanks!

## Convergence for replicated directory trees

Late last week we were able to achieve convergence in our randomized tests of  replicated directory trees. As I mentioned in the last update, the biggest challenge was the possibility of concurrent moves introducing cycles. Our proposed solution of breaking cycles via synthetic "fixup" operations worked out well, but determining exactly *which* fixup operations to generate was still a challenging problem.

In certain diabolical scenarios, reverting a move to break one cycle could end up introducing a second cycle. By reverting *multiple* moves, however, it should always be possible to end up with a directory tree that is free of cycles, and so that's what we do. Whenever a cycle is detected, we continually revert the most recent move that contributes to that cycle, ignoring any moves that have already been reverted. Eventually, we're guaranteed to end up with a tree that's free of cycles. Though we don't have a formal proof to back up our intuition, [we've been unable to find a failing scenario over a million randomized trials](https://github.com/atom/xray/blob/6c49587aad45d7880449668e4b882267435ff763/eon/src/fs2.rs#L1523), and we're ready to move forward.

We applied the same "fixup" strategy to recover from directory name conflicts as well. When we attempt to insert a directory entry whose name conflicts with an existing entry, we compare the entries' Lamport timestamps and replica ids to select an entry that gets to keep the existing name. For the other entry, we append `~` characters until we find a name that does not conflict and synthesize a rename operation. In a real-time scenario, this situation should almost never occur, but if it does, renaming one of the directories means we can mirror the state of the CRDT to the file system without losing data. The users can then decide how to deal with the situation by deleting one of the directories or merging their contents.

## Interacting with the file system

For our convergence results to be useful outside the realm of automated tests, we need to communicate changes to and from the file system. That presents its own set of challenges, since we can't rely on our internal representation always being perfectly synchronized with the state of the disk. After confusing ourselves a bit too much trying to devise a strategy for file system synchronization that could cover every possible scenario, we've decided to focus on a few narrowly-defined situations on the critical path to a working demo.

* Read a tree into a new index: When the Eon daemon starts, we will need to read the current state of the tree into our internal representation.
* Write an index to a file system tree: When you want to clone a remote replica, we need to write its initial state to your local disk.
* Update an index from a tree: Once the daemon is started, we want to watch the file system for changes. When we detect a change, we will scan the directory tree to determine which directories have been inserted, removed, or moved.
* Write incoming operations to the disk: As operations come in, we interpret them relative to our internal index and translate them into writes to the file system.

For now, we've decided to rely on the fact that files and directories get associated with unique inode numbers in order to detect moves. In our previous attempt, we were hoping to not fully rely on inodes in hopes of covering cases such as the entire repository being recursively copied or another system like Git manipulating the file tree. Now we've decided we will deal with those scenarios in a separate pass once we get the basic scenario working. Tracking the mapping between our internal file identifiers and inodes makes everything much simpler.

One thing that makes it challenging (if not impossible) to mirror changes to the file system perfectly is the inability to perform file system operations atomically. When we receive a move operation from the network, we'll resolve abstract identifiers in the operation to actual paths on the local disk. If the disk's contents have changed in the meantime and we haven't heard about it, there's a potential for these paths to be wrong. To mitigate this issue, we will always confirm that the relevant paths exist and have the expected inode numbers before applying a remote operation. If we detect that our index has fallen behind the contents of the disk, we will defer handling the operation until the next update.

However, even if we determine that our index is consistent with the disk, this determination isn't atomic. In the microseconds between checking for consistency and performing the write, another change might invalidate our conclusion and cause the operation to fail. Worse, a change might cause the same paths to point to different inodes, meaning the operation would succeed but apply to different paths. Luckily, we anticipate this sort of situation to be extremely rare. It could only happen if a file at a given path was replaced with another in the moment between our consistency check and actually writing the operation. It might lead to surprising results, but we don't think the consequences are catastrophic.

Dealing with all of these problems and getting changes to and from the file system will be our focus for this week.


================================================
FILE: docs/updates/2018_07_31.md
================================================
# Update for July 31, 2018

## Contributions

[@Aerijo](https://github.com/Aerijo) encountered some confusion and took it upon himself to [update our contributing guide](https://github.com/atom/xray/pull/118) to ensure others wouldn't suffer the same fate. We really appreciate these kinds of improvements.

## Batched conflict resolution

As I mentioned in last week's update, having achieved convergence for replicated directory trees, last week we started down the path of mirroring changes from our internal CRDT-based representation to the underlying file system. After implementing file system reads and starting on randomized tests, we quickly realized that our previous mental model was incomplete.

In our previous tests of convergence, we applied operations to our in-memory representation one at a time, moving, inserting, and deleting each directory in serial. However, when scanning changes from the disk, this serial approach is impossible. We only see a snapshot of the file system's latest state, which could have been produced by a variety of different sequences of individual operations.

Consider the following directory structure, with two different directories that are both named "b". We'll label them `b(1)` and `b(2)` in our example to clearly identify them:

```
a/
a/b(1)
b(2)/
```

The next time we scan the file system, we observe that the directory structure has changed to the following:

```
a/
a/b(2)
b(1)/
```

The two directories named `b` have swapped their positions. If we naively apply the operations derived from this swap one at a time on a remote replica, we'll end up creating name conflicts. As I've discussed previously, we resolve name conflicts created by concurrent operations by appending a tilde character to one of the conflicting names. But in this case, appending a tilde would be incorrect, because the final state of the tree that we are trying to produce contains no actual conflicts.

To avoid spurious conflict resolutions, we moved from resolving conflicts after each operation to resolving conflicts after applying arbitrary batches of operations. It took a couple days to iron out all of the new issues and edge cases with this new approach in randomized testing, which took us until last Thursday. Finally, we managed to achieve convergence with the new approach to conflict resolution in a million randomized trials of 5 different peers applying 20 operations.

## Batched writes to the file system

The batched nature of operation application presented a puzzle for file system writes as well. Previously, we had planned on applying the effects of each operation as it arrived, but now we realized that wouldn't work. We needed to apply a batch of operations to the tree, resolve conflicts, and *only then* write changes in the new state of tree to the file system. Unlike our internal representation, which can temporarily tolerate intermediate states containing conflicts and cycles, each operation applied to the file system must ensure that the tree remains acyclic and free of name conflicts.

We ended up converging on the following approach: We maintain a set of the internal identifiers of all files we have inserted, moved, or removed in the course of applying a batch of operations. We then sort insertions and moves by the depth of the inserted path in the new tree and sort deletions last. By performing shallower insertions first, we ensure that the parent of any directory we are trying to insert always exists.

By performing shallower moves first, we ensure that we don't accidentally create cycles while rearranging directories. We don't have a formal proof, and we may need more empirical verification to be completely confident, but the intuition is as follows: A cycle can only be created by moving a directory downward to become one of its own descendants. Because we've broken cycles in the new tree, we should never encounter a situation in which a directory has been moved to become its own descendant in the final state of the new tree. A combination of moves could end up creating a cycle momentarily, but this cycle could only be created by moving a directory deeper in the tree. If we perform upward moves first, by the time we would be attempting to move a directory into one of its own descendants, we should have already moved that descendant to an equal or shallower depth. At least that's our intuition, and evidence so far is that it works.

Finally, we need to deal with temporary name conflicts that can occur when directories are shuffled around. We've opted to take an extremely simple approach. When performing a move on the file system would create a name conflict, we append tildes until we find a free name and record the fact that we have done so. When all operations have been applied, we go back and clean up, renaming directories with appended tildes back to their desired names. At this point, all of the conflicts should be resolved, and so we can do this without risk of conflict.

## Dealing with concurrency

The above approach worked in randomized trials at the end of last week, but we knew we were only solving part of the problem. Our initial implementation assumed that we were the only process writing to the file system. In reality, the file system can change out from under us at any time, meaning that we could be attempting to update the file system based on an outdated understanding of the file system's state.

To deal with this, before we integrate a batch of operations into our tree, we clone the tree's current state and as the `old_tree`. This represents our best guess as to the current state of the underlying file system. We then update the new tree, resolve conflicts, and start writing. For each file we need to update, we use the `old_tree` to determine the current location of the relevant directories on disk. Assuming a directory still exists at the path in question, we compare inode numbers to ensure it has the proper identity. Assuming our understanding of all the relevant paths is up to date, we can proceed with the file system write and update the `old_tree` accordingly.

If anything goes wrong, such as the path not existing, the path's inode not matching, or the write operation returning some kind of error, we need to pause the entire process and update our understanding of the old tree via a file system scan. As we integrate changes to the old tree, we produce operations which need to be applied to the new tree. Moves, deletions or conflict resolutions could end up changing the nature of operations we have still yet to write, requiring us to refresh and re-sort our pending writes after the old tree is updated.

At the time of writing, we have yet to achieve convergence in the presence of full concurrency with the underlying file system, but it seems like we are getting close. Hopefully we'll get there by the end of this week.

## Q3 Demo

Our focus during Q2 has been figuring out how to achieve optimistic replication on the entire file system as well as persistence of all operations, and we've nearly done it. Once we achieve this abstraction, we plan to shift our focus to showcasing its capabilities in a new demo.

We're still not clear on the details, but the basic idea is that you should be able to open a repository in Xray, then open a "streams" panel to view the latest state of all other working copies of that repository from other developers working in Xray, whether or not they are currently online. If a stream is being actively edited, you'll be able to collaborate. If that stream's author is offline, you'll be able to pick up where they left off. You'll also be able to fork a stream, though we probably won't finish merging before the end of the quarter.

We feel confident we can achieve that basic experience, but if we have time, we'd like to restore the conversation panel now that we will be able to persist anchors over the lifetime of a repository. We'd also like to find other ways to show off our operation-level history, such as the ability to play back operations.

We still plan for Eon (or whatever we end up calling it; I'm not sure if I like the name) to be a standalone tool that can integrate with other editors. But we need to drive its development with a real product experience, and the best way to do that is by producing a working demo.

## Vacation

Antonio is on vacation this week and next week, and I'll also be out next week to spend some quality time with my family. Due to this, expect a 2-week communication gap. We'll come back recharged to slash through randomized test failures and produce a demo of a whole new approach to collaboration. Thanks for reading!


================================================
FILE: docs/updates/2018_08_21.md
================================================
# Update for August 21, 2018

## *Eon* is now *Memo*

I chose the name *Eon* fairly hastily and ended up kind of disliking it. I wanted to change it almost immediately, but decided to hold off until I felt sure about its replacement. *Memo* is one character longer but just sounds better to me and reflects the system's ability to record every keystroke. It's kinda silly to worry this much about a name, but I just needed to change it. Now it's done. Moving on.

## Convergence for directory trees

The bigger news is that we've finally achieved convergence in our randomized tests of replicated directory trees. The problem ended up being way harder than we imagined. The final challenge was to fully simulate the possibility for the file system to change at any time, including during a directory scan.

We are cautiously optimistic that the worst of the algorithmic challenges could be behind us. Weeks of wading through randomized test failures has been a bit monotonous, but hopefully we can pick up some momentum building on top of this abstraction.

## Supporting text files and evolving the high-level structure

The next step is to add support for files to the directory tree, which we think should be easier. Much of what we learned dealing with pure directories can be applied to files, and since files are always leaf nodes we shouldn't need to deal with cycles. We *do* need to deal with hard links, however, which should add some complexity.

Supporting files also means we need to figure out the relationship between the CRDT that maintains the state of the directory tree and the CRDTs that contain the contents of individual text files.

This week seems like the right time to zoom out and get a bit more clarity on the system's higher level design. Until we had a working CRDT for directory trees that felt premature, but now it seems like understanding the big picture a bit better might inform the relationship between the directory tree and individual text files.

We've gone back and forth on whether we should try to decouple them, but for now we think we're going to try a more integrated approach where the directory tree CRDT has explicit knowledge of the file CRDTs. For now, we've decided to wrap both concerns in a single type called a `Timeline`, which will represent the full state of a working tree as it evolves forward in time. A `Repository` will contain multiple timelines which can evolve in parallel, fork, and eventually merge.

There's still quite a bit to figure out though. How will we route operations to and from buffers? What will the ownership structure look like? How can we ensure that performing I/O doesn't interfere with the responsiveness of the system? We'll hopefully have some conclusions about those questions and more to share in the next update.


================================================
FILE: docs/updates/2018_08_28.md
================================================
# Update for August 28, 2018

## Convergence for files and hard links

As predicted in the [last update](./2018_08_21.md), adding support for files and hard links to our directory tree CRDT went smoothly, and we achieved convergence in our randomized tests on Monday. Because hard links make it possible for the same file to appear in multiple locations, many code paths needed to be updated to work in terms of *references* rather than files. Happily, we had already anticipated hard links by allowing a file to be associated with multiple parent refs, so the path was mostly paved. Once we add support for file contents and confirm that everything works in an end-to-end test, we plan to post an in-depth write-up on the directory tree CRDT and do a documentation pass on the [timeline module](../../memo/src/timeline.rs).

## Next up, buffers

The file support added last week assumes that all files are empty. To allow files to be associated with editable content, we're adapting the [`buffer`](../../xray_core/src/buffer.rs) module from `xray_core` to work with Memo's [new B-tree](../../memo/src/btree.rs). The primary difference between the previous B-tree implementation and the new one is support for storing the tree's nodes in a database. This will allow us to store a file's entire history without loading old fragments into memory, but it also means that many methods now have the potential to perform I/O with the database and encounter I/O errors.

We'll need to adjust the `Buffer` APIs slightly to account for this potential. For example, we can no longer return an iterator that implements the `Iterator` trait, since `next` would need to return a `Result` type. We're also dropping some of the previous buffer's support for Xray's RPC system because we anticipate dealing with network interactions differently in Memo. We don't have complete clarity on our plans for dealing with networking just yet, but it makes sense to keep our assumptions minimal at this stage.

Once we get buffers implemented against our new B-tree, we'll need to integrate them into our timeline. We plan to maintain a mapping between file ids and the buffers that contain their contents, but the details will become clearer once we get into it. Buffers will need to be integrated with up to three distinct sources of I/O: the file system for reading/saving contents, the network for collaboration, and the database for history persistence. It should be a fun design problem to give them a convenient API while addressing all of those concerns.


================================================
FILE: docs/updates/2018_09_14.md
================================================
# Update for September 14, 2018

It's been an intense couple of weeks, but we're coming out of it with more clarity than ever on the future direction of Memo. We're entering a new phase of the project where we distill the research of the last few months into a usable form. Thanks for your patience with the radio silence the last couple of weeks.

## Embracing Git

Our previous vision for Memo was to store the full operational history for the repository in a global database, so that each file's full history would be available in a single structure dating back to its creation. This would essentially duplicate the role of Git as a content tracker for the repository, but with a much more fine-grained resolution. It may eventually make sense to build a global operation index to enable advanced features and analysis, but I don't think it makes sense to conceive of such an index as an independent version control system. For async collaboration, CRDTs probably won't offer enough advantages to induce people to switch away from Git. Even if we managed to build such a system, it would always need to interoperate with Git. So we may as well embrace that reality and build on top of Git. We can then focus on the area where CRDTs have their greatest strength: real-time collaboration and recording the fine-grained edits that occur *between* Git commits.

Augmenting Git is definitely something I've considered in the past, but it's finally becoming clearer how we can achieve it. We will start by packaging the previous months' work into a library that is similar to `teletype-crdt`. With Teletype, you work with individual buffers. Each local edit returns one or more operations to apply on remote replicas, and applying remote operations returns a diff that describes the impact of those operations on the local replica. Memo will expand the scope of this abstraction from individual buffers to the working tree, but it won't represent the full state of this tree in the form of operations. Instead, we'll exploit the fact that Git commits provide a common synchronization point. The library will expect any data that's committed to the Git repository to be supplied separately from the operations.

By making the CRDT representation sparse and leaning on Git to synchronize the bulk of the working tree, we reduce the memory footprint of the CRDT to the point where it can reasonably be kept resident in memory. This also bounds the bandwidth required to replicate the full structure, which obviates the need for complex partial data fetching schemes that we were considering previously. This in turn greatly simplifies backend infrastructure. Because a sparse representation should always be small enough to fully reconstruct from raw operations on the client, server side infrastructure shouldn't need to process operations in any way other than simply storing them based on the identifier of the patch to which they apply.

## The challenge of undo

One big obstacle to making this patch-based representation work is undo. In `teletype-crdt`, we implement undo by associating every operation with a counter. If an operation's undo counter is odd, we consider the operation to be undone and therefore invisible. If the operation's undo counter is even, we consider the operation to be visible. If two users undo or redo the same operation concurrently, they'll both assign its undo count to the same number, which preserves both users' intentions of undoing the operation and avoids their actions doubling up or cancelling each other out, which could occur in some other schemes. However, implementing undo in this way comes with a cost, which is that in order for me to undo an operation that is present in my local history, I need to rely on that operation being present in the history of all of my collaborators. This approach to undo combines poorly with resetting to an empty CRDT on each commit, because it forces everyone to clear their undo stack after committing since there won't be any way to refer to prior operations in order to update their undo counters.

This felt like a show-stopper to me until I had a conversation with [@jeffrafter](https://github.com/jeffrafter) about his team's experience using `teletype-crdt` in [Tiny](https://tttiny.com/). I don't have a perfect understanding of the details of their approach, but they essentially bypass Teletype's built-in undo system and maintain their own history on each client independently of the CRDT. When a user performs an undo, they simply apply its to the current CRDT and broadcast it as a new operation. When I asked about some of the more diabolical concurrency scenarios that the counters were designed to eliminate, Jeff simply replied that it's working for them in practice.

Inspired by their experience, I have a hunch that we can implement undo similarly in our library. For each buffer, we can maintain two CRDTs. One will serve as a local non-linear history that allows us to understand the effects of undoing operations that aren't the most recent change to the buffer. We'll perform undos against this local history first, then apply their affects to a CRDT that starts at the most recent commit. This will generate remote operations we know can be cleanly applied by all participants. The local history can be retained across several commits and even be stored locally. By fetching operations from previous commits, we could even construct such a history for clients that are new to the collaboration.

## Stable file references

We need to be able to refer to files in a universal way, but with this hybrid approach, only *new* files are assigned identifiers by operations. This stumped us for a bit, until an obvious solution occurred to us. The set of paths in the base Git commit is the same for every replica, so we can sort these paths lexicographically and assign each an identifier based on its position in this fixed order. Internally, file identifiers are a Rust enum with two possible variants, `Base`, which wraps a `u64`, and `New`, which wraps a local timestamp generated on the replica that created the file.

## The big picture

By being agnostic to plumbing and building a library that operates purely in terms of operations and data, this software should be useful in a broader array of applications. We plan to distribute a WebAssembly version to enable collaboration in browser-based environments, along with a native executable that can talk to editors and synchronize our CRDT with an underlying file system like we originally envisioned. The operations can serve as a kind of common real-time collaboration protocol. As long as an application can send and receive operations and feed them into this library, it should be capable of real-time collaboration with other applications.

In light of these shifts in our thinking, I've updated the [Memo README](../../README.md) to reflect the current state of the world. Some details about the implementation have been dropped, but I plan to reintroduce them over time as our implementation stabilizes. At some point soon, it may make sense to again pull Memo out into its own repository that is separate from Xray. If that happens, I'll keep everyone posted here.


================================================
FILE: docs/updates/2018_10_02.md
================================================
# Update for October 2, 2018

## Shipped an initial light client

Last week, we [shipped](https://github.com/atom/xray/pull/135) an initial version of Memo JS, a light-client implementation of Memo that can be used as a library in web-based applications. To start with, we're assuming that the file system is completely virtual and that all changes are routed directly through the library. This meant that we ended up temporarily shelving a lot of the work we did to synchronize our tree CRDT with an external file system, but we still plan to take advantage of that research in order to build the full client that's capable of observing an external repository. Shipping the light client first will hopefully let us get some feedback and iterate on other aspects of the protocol's design before introducing the complexity of interoperating with an external file system.

## Next, Git operations

Currently, a Memo `WorkTree` always starts at a base commit and builds forward indefinitely with operations. We assume that application code will be responsible for tearing the work tree down and rebuilding it following a commit. The next step is pull this concern into Memo itself and to allow the base commit of a replicated work tree to *change* over time due to operations on the underlying repository such as committing, resetting, and checking out different branches.

We're still in the middle of figuring this out. It's murky and our thinking is still in flux. We're focused on the light client currently, which simplifies our API and reduces complexity, but we still want a design that will work when we do eventually synchronize to the file system. It's somewhat unclear whether we should just start focusing on integrating with the file system now, or alternatively completely ignore the concerns of the file system and hope we can make adjustments later. For now though, here's what is emerging.

### Epochs

We divide the evolution of the work tree into *epochs*. Each epoch begins with a specific commit from the underlying repository that gives all replicas a common frame of reference, then applies additional operations on top to represent uncommitted changes in that epoch. There is one and only one *active epoch* at any time on a given replica. All operations are tagged with an epoch id, and the local counters used to identify operations are reset to zero at the start of each epoch. Someone joining the collaboration should only need to fetch operations associated with the most recent epoch.

When a user performs a local Git operation such as a commit or a reset, they broadcast the creation of a new epoch. Because users can create new epochs concurrently, we always honor the epoch creation with the most recent Lamport timestamp at every replica, which will provide an arbitrary but consistent behavior for concurrent epoch creations while also respecting causality in the sequential case.

### Resets

Collaborators can reset the HEAD of the working copy to an arbitrary ref. In that case, we need to create a new epoch. Depending on the nature of the reset and the state of the file system, there may be uncommitted changes on disk. We'd also like to incorporate the concept of unsaved changes when we integrate with the file system. Both uncommitted changes and unsaved changes will need to be translated into synthetic operations that build upon the new epoch's base commit.

When the epoch creation arrives at remote replicas, it seems like they will have no choice but to perform I/O in order to scan the epoch's base entries into the tree. The base state of open buffers may also need to be re-read, and some of these open buffers may be for files that no longer exist in the new epoch's base commit.

This is where things start to feel pretty messy and confusing. What happens to these "untethered" buffers? Do we empty out the tree and build it back up as we perform I/O on the base entries, or do we preserve the old state until the new state is ready. How do races with the file system complicate all of this?

### Commits

Commits create a new epoch whose state is derived from a previous epoch, although due to the potential for concurrent commits and resets, a commit doesn't always derive from the active epoch on a given replica. Ignoring the potential for partial staging for the moment, when a user creates a commit, we can characterize what they committed via a version vector that includes all observed operations in the current epoch.

If a replica receives a commit based on the active epoch (which should be the most common case), we should be able to determine their base entries without performing I/O. This is because the state that was committed should already be available as a subset of operations they have already seen, as characterized by the version vector. This would allow us to update the tree to its new state synchronously in a very common case.

On the other hand, there's no guarantee that a commit is going to based on the active epoch thanks to diabolical concurrency scenarios, and this seems to mean that we may end up needing to do I/O anyways in some scenarios. That makes us wonder whether we should focus first on the ability to reset the base commit in arbitrary ways and treat commits as a special case of that.

## Conclusion

This is a hard problem. We've made it through one wave of complexity to encounter another, and presumably that will continue. Every decision seems to be entangled with everything else, and even this summary just scratches the surface of the thought process behind this problem. But despite the daunting complexity, I'm still excited by the idea of a fully-replicated Git working copy. Git operations are the next summit to climb, and I imagine there will be more wilderness before we can settle in the fertile valley of conflict free replicated paradise.


================================================
FILE: memo_core/Cargo.toml
================================================
[package]
name = "memo_core"
version = "0.1.0"
authors = ["Antonio Scandurra <as-cii@github.com>", "Nathan Sobo <nathan@github.com>"]
edition = "2018"

[dependencies]
diffs = "0.3"
lazy_static = "1.0"
flatbuffers = "0.5"
futures = "0.1"
serde = "1.0"
serde_derive = "1.0"
smallvec = "0.6.1"
uuid = { version = "0.7", features = ["serde"] }

[dev-dependencies]
futures-cpupool = "0.1"
rand = "0.3"
uuid = { version = "0.7", features = ["serde", "u128"] }


================================================
FILE: memo_core/README.md
================================================
# Memo – Real-time collaboration for Git

**This project is a work in progress. This README defines the vision, but it isn't fully implemented yet.**

On its own, Git can only synchronize changes between clones of a repository after the changes are committed, which forces an asynchronous collaboration workflow. A repository may be replicated across several machines, but the working copies on each of these machines are completely independent of one another.

Memo's goal is to extend Git to allow a single working copy to be replicated across multiple machines. Memo uses conflict-free replicated data types (CRDTs) to record all uncommitted changes for a working copy, allowing changes to be synchronized in real time across multiple replicas as they are actively edited. Memo also maintains an operation-based record of all changes, augmenting Git's commit graph with the fine-grained edit history behind each commit.

Memo is divided into the following major components:

* Protocol: Memo can be thought of as a protocol for real-time change synchronization between working copies that will eventually be open for anyone to implement.

* Library: Memo provides a reference library implementation written in Rust that produces and consumes the Memo protocol messages to synchronize working trees. We plan to ship a "light client" version of the library that compiles to WebAssembly and exposes a virtual file system API, as well as a full version based on Libgit2 that synchronizes with a full replica on the local file system. The libraries could be used in web- or desktop-based editors to enable real-time collaboration on a shared working copy of a Git repository.

* Executable daemon: Memo will provide an executable (also written in Rust) that runs as a daemon process on the local machine. It will synchronize with an underlying file system and expose an RPC interface to support integrations with a variety of editors for collaborative buffer editing.

* Xray: Memo spun out of Xray, which was an experiment to build a collaborative text editor. After the library stabilizes, we may decide to resume development of Xray as a first-class collaborative editor that is designed with Memo in mind. For now, we view the development of the more generalized technology as more important than building a new editor.

Interesting features / design priorities are as follows:

* Based on Git: When it comes to async collaboration and coarse-grained change synchronization, it's hard to beat Git. Memo doesn't try. Our goal is to enable Git users to share a single working copy and relay changes in real time. We may implement the ability to "fork" the state of a working copy, but we don't plan to implement asynchronous features such as branching and merging in terms of conflict-free replicated data types. For that you will continue to use Git. We will strive not to send or store any data that can already be derived from the state of the Git repository.

* Distributed: Like Git, Memo is fully distributed. This means that no replica is privileged over any other. No specific network topology will be enforced by our core algorithms and it will be possible to disseminate operations in arbitrary ways.

* Covers the whole working tree: Memo will merge concurrent edits to files along with modifications of the file system tree. One person can edit a file while another person moves it to a new directory, etc.

* Open and general purpose: We want Memo to feel similar to Git, a tool that can be integrated in a variety of workflows and environments. We may build more centralized experiences on top of it, but the core protocol should remain open and decentralized.

* More than just the source code: One of Memo's primary use cases is real-time collaboration, but effectively collaborating on source code often requires support from the environment to compile, run tests, statically analyze, etc. We intend to extend Memo's protocol to support primitives such as streams and shared buffers, which could support log output or a shared terminal, and annotations, which could support static analysis. An ideal scenario might see two developers with full replicas collaborating with a third developer in a browser, all viewing diagnostics generated by a language server running against a replica in the cloud and viewing test output from another machine.

A fundamental goal is to make the distinction between physical machines less relevant during the actual process of writing code. Today, most code is developed locally, while some code may be developed in cloud-based IDEs. It shouldn't actually matter *where* the working tree is located, and it might be replicated to multiple machines simultaneously which are all contributing something to the overall experience of the participating developers.


================================================
FILE: memo_core/rustfmt.toml
================================================
edition = "2018"


================================================
FILE: memo_core/script/compile_flatbuffers
================================================
#!/bin/bash

flatc --rust -o src/serialization src/serialization/schema.fbs

# Workaround for incorrect code generation by flatc
echo "use flatbuffers::EndianScalar;" >> src/serialization/schema_generated.rs


================================================
FILE: memo_core/src/btree.rs
================================================
use smallvec::SmallVec;
use std::cmp::Ordering;
use std::fmt;
use std::ops::{Add, AddAssign};
use std::sync::Arc;

#[cfg(test)]
const TREE_BASE: usize = 2;
#[cfg(not(test))]
const TREE_BASE: usize = 16;

pub trait Item: Clone + Eq + fmt::Debug {
    type Summary: for<'a> AddAssign<&'a Self::Summary> + Default + Clone + fmt::Debug;

    fn summarize(&self) -> Self::Summary;
}

pub trait KeyedItem: Item {
    type Key: Dimension<Self::Summary>;

    fn key(&self) -> Self::Key;
}

pub trait Dimension<Summary: Default>:
    for<'a> Add<&'a Self, Output = Self> + for<'a> AddAssign<&'a Self> + Ord + Clone + fmt::Debug
{
    fn from_summary(summary: &Summary) -> Self;

    fn default() -> Self {
        Self::from_summary(&Summary::default()).clone()
    }
}

#[derive(Debug, Clone)]
pub struct Tree<T: Item>(Arc<Node<T>>);

#[derive(Debug)]
pub enum Node<T: Item> {
    Internal {
        height: u8,
        summary: T::Summary,
        child_summaries: SmallVec<[T::Summary; 2 * TREE_BASE]>,
        child_trees: SmallVec<[Tree<T>; 2 * TREE_BASE]>,
    },
    Leaf {
        summary: T::Summary,
        items: SmallVec<[T; 2 * TREE_BASE]>,
    },
}

#[derive(Clone)]
pub struct Cursor<T: Item> {
    tree: Tree<T>,
    stack: SmallVec<[(Tree<T>, usize, T::Summary); 16]>,
    summary: T::Summary,
    did_seek: bool,
    at_end: bool,
}

pub struct FilterCursor<F: Fn(&T::Summary) -> bool, T: Item> {
    cursor: Cursor<T>,
    filter_node: F,
}

#[derive(Eq, PartialEq)]
pub enum SeekBias {
    Left,
    Right,
}

#[derive(Debug)]
pub enum Edit<T: KeyedItem> {
    Insert(T),
    Remove(T),
}

impl<T: Item> Tree<T> {
    pub fn new() -> Self {
        Tree(Arc::new(Node::Leaf {
            summary: T::Summary::default(),
            items: SmallVec::new(),
        }))
    }

    pub fn from_item(item: T) -> Self {
        let mut tree = Self::new();
        tree.push(item);
        tree
    }

    #[allow(dead_code)]
    pub fn items(&self) -> Vec<T> {
        let mut items = Vec::new();
        let mut cursor = self.cursor();
        cursor.descend_to_first_item(self.clone(), |_| true);
        loop {
            if let Some(item) = cursor.item() {
                items.push(item);
            } else {
                break;
            }
            cursor.next();
        }
        items
    }

    pub fn cursor(&self) -> Cursor<T> {
        Cursor::new(self.clone())
    }

    pub fn filter<F>(&self, filter_node: F) -> FilterCursor<F, T>
    where
        F: Fn(&T::Summary) -> bool,
    {
        FilterCursor::new(self, filter_node)
    }

    #[allow(dead_code)]
    pub fn first(&self) -> Option<T> {
        self.leftmost_leaf().0.items().first().cloned()
    }

    pub fn last(&self) -> Option<T> {
        self.rightmost_leaf().0.items().last().cloned()
    }

    pub fn extent<D: Dimension<T::Summary>>(&self) -> D {
        match self.0.as_ref() {
            Node::Internal { summary, .. } => D::from_summary(summary).clone(),
            Node::Leaf { summary, .. } => D::from_summary(summary).clone(),
        }
    }

    pub fn summary(&self) -> T::Summary {
        match self.0.as_ref() {
            Node::Internal { summary, .. } => summary.clone(),
            Node::Leaf { summary, .. } => summary.clone(),
        }
    }

    #[cfg(test)]
    pub fn is_empty(&self) -> bool {
        match self.0.as_ref() {
            Node::Internal { .. } => false,
            Node::Leaf { items, .. } => items.is_empty(),
        }
    }

    pub fn extend<I>(&mut self, iter: I)
    where
        I: IntoIterator<Item = T>,
    {
        let mut leaf: Option<Node<T>> = None;

        for item in iter {
            if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE {
                self.push_tree(Tree(Arc::new(leaf.take().unwrap())));
            }

            if leaf.is_none() {
                leaf = Some(Node::Leaf::<T> {
                    summary: T::Summary::default(),
                    items: SmallVec::new(),
                });
            }

            let leaf = leaf.as_mut().unwrap();
            *leaf.summary_mut() += &item.summarize();
            leaf.items_mut().push(item);
        }

        if leaf.is_some() {
            self.push_tree(Tree(Arc::new(leaf.take().unwrap())));
        }
    }

    pub fn push(&mut self, item: T) {
        self.push_tree(Tree::from_child_trees(vec![Tree(Arc::new(Node::Leaf {
            summary: item.summarize(),
            items: SmallVec::from_vec(vec![item]),
        }))]))
    }

    pub fn push_tree(&mut self, other: Self) {
        let other_node = other.0.clone();
        if !other_node.is_leaf() || other_node.items().len() > 0 {
            if self.0.height() < other_node.height() {
                for tree in other_node.child_trees() {
                    self.push_tree(tree.clone());
                }
            } else if let Some(split_tree) = self.push_tree_recursive(other) {
                *self = Self::from_child_trees(vec![self.clone(), split_tree]);
            }
        }
    }

    fn push_tree_recursive(&mut self, other: Tree<T>) -> Option<Tree<T>> {
        match Arc::make_mut(&mut self.0) {
            Node::Internal {
                height,
                summary,
                child_summaries,
                child_trees,
                ..
            } => {
                let other_node = other.0.clone();
                *summary += other_node.summary();

                let height_delta = *height - other_node.height();
                let mut summaries_to_append = SmallVec::<[T::Summary; 2 * TREE_BASE]>::new();
                let mut trees_to_append = SmallVec::<[Tree<T>; 2 * TREE_BASE]>::new();
                if height_delta == 0 {
                    summaries_to_append.extend(other_node.child_summaries().iter().cloned());
                    trees_to_append.extend(other_node.child_trees().iter().cloned());
                } else if height_delta == 1 && !other_node.is_underflowing() {
                    summaries_to_append.push(other_node.summary().clone());
                    trees_to_append.push(other)
                } else {
                    let tree_to_append = child_trees.last_mut().unwrap().push_tree_recursive(other);
                    *child_summaries.last_mut().unwrap() =
                        child_trees.last().unwrap().0.summary().clone();

                    if let Some(split_tree) = tree_to_append {
                        summaries_to_append.push(split_tree.0.summary().clone());
                        trees_to_append.push(split_tree);
                    }
                }

                let child_count = child_trees.len() + trees_to_append.len();
                if child_count > 2 * TREE_BASE {
                    let left_summaries: SmallVec<_>;
                    let right_summaries: SmallVec<_>;
                    let left_trees;
                    let right_trees;

                    let midpoint = (child_count + child_count % 2) / 2;
                    {
                        let mut all_summaries = child_summaries
                            .iter()
                            .chain(summaries_to_append.iter())
                            .cloned();
                        left_summaries = all_summaries.by_ref().take(midpoint).collect();
                        right_summaries = all_summaries.collect();
                        let mut all_trees =
                            child_trees.iter().chain(trees_to_append.iter()).cloned();
                        left_trees = all_trees.by_ref().take(midpoint).collect();
                        right_trees = all_trees.collect();
                    }
                    *summary = sum(left_summaries.iter());
                    *child_summaries = left_summaries;
                    *child_trees = left_trees;

                    Some(Tree(Arc::new(Node::Internal {
                        height: *height,
                        summary: sum(right_summaries.iter()),
                        child_summaries: right_summaries,
                        child_trees: right_trees,
                    })))
                } else {
                    child_summaries.extend(summaries_to_append);
                    child_trees.extend(trees_to_append);
                    None
                }
            }
            Node::Leaf { summary, items, .. } => {
                let other_node = other.0;

                let child_count = items.len() + other_node.items().len();
                if child_count > 2 * TREE_BASE {
                    let left_items;
                    let right_items: SmallVec<[T; 2 * TREE_BASE]>;

                    let midpoint = (child_count + child_count % 2) / 2;
                    {
                        let mut all_items = items.iter().chain(other_node.items().iter()).cloned();
                        left_items = all_items.by_ref().take(midpoint).collect();
                        right_items = all_items.collect();
                    }
                    *items = left_items;
                    *summary = sum_owned(items.iter().map(|item| item.summarize()));
                    Some(Tree(Arc::new(Node::Leaf {
                        summary: sum_owned(right_items.iter().map(|item| item.summarize())),
                        items: right_items,
                    })))
                } else {
                    *summary += other_node.summary();
                    items.extend(other_node.items().iter().cloned());
                    None
                }
            }
        }
    }

    fn from_child_trees(child_trees: Vec<Tree<T>>) -> Self {
        let height = child_trees[0].0.height() + 1;
        let mut child_summaries = SmallVec::new();
        for child in &child_trees {
            child_summaries.push(child.0.summary().clone());
        }
        let summary = sum(child_summaries.iter());
        Tree(Arc::new(Node::Internal {
            height,
            summary,
            child_summaries,
            child_trees: SmallVec::from_vec(child_trees),
        }))
    }

    fn leftmost_leaf(&self) -> Tree<T> {
        match *self.0 {
            Node::Leaf { .. } => self.clone(),
            Node::Internal {
                ref child_trees, ..
            } => child_trees.first().unwrap().leftmost_leaf(),
        }
    }

    fn rightmost_leaf(&self) -> Tree<T> {
        match *self.0 {
            Node::Leaf { .. } => self.clone(),
            Node::Internal {
                ref child_trees, ..
            } => child_trees.last().unwrap().rightmost_leaf(),
        }
    }
}

impl<T: KeyedItem> Tree<T> {
    pub fn insert(&mut self, item: T) {
        let mut cursor = self.cursor();
        let mut new_tree = cursor.slice(&item.key(), SeekBias::Left);
        new_tree.push(item);
        new_tree.push_tree(cursor.suffix::<T::Key>());
        *self = new_tree;
    }

    pub fn edit(&mut self, edits: &mut [Edit<T>]) {
        if edits.is_empty() {
            return;
        }

        edits.sort_unstable_by_key(|item| item.key());

        let mut cursor = self.cursor();
        let mut new_tree = Tree::new();
        let mut buffered_items = Vec::new();

        cursor.seek(&T::Key::default(), SeekBias::Left);
        for edit in edits {
            let new_key = edit.key();
            let mut old_item = cursor.item();

            if old_item
                .as_ref()
                .map_or(false, |old_item| old_item.key() < new_key)
            {
                new_tree.extend(buffered_items.drain(..));
                let slice = cursor.slice(&new_key, SeekBias::Left);
                new_tree.push_tree(slice);
                old_item = cursor.item();
            }
            if old_item.map_or(false, |old_item| old_item.key() == new_key) {
                cursor.next();
            }
            match edit {
                Edit::Insert(item) => {
                    buffered_items.push(item.clone());
                }
                Edit::Remove(_) => {}
            }
        }

        new_tree.extend(buffered_items);
        new_tree.push_tree(cursor.suffix::<T::Key>());

        *self = new_tree;
    }
}

impl<T: Item> Node<T> {
    fn is_leaf(&self) -> bool {
        match self {
            Node::Leaf { .. } => true,
            _ => false,
        }
    }

    fn height(&self) -> u8 {
        match self {
            Node::Internal { height, .. } => *height,
            Node::Leaf { .. } => 0,
        }
    }

    fn summary(&self) -> &T::Summary {
        match self {
            Node::Internal { summary, .. } => summary,
            Node::Leaf { summary, .. } => summary,
        }
    }

    fn child_summaries(&self) -> &[T::Summary] {
        match self {
            Node::Internal {
                child_summaries, ..
            } => child_summaries.as_slice(),
            Node::Leaf { .. } => panic!("Leaf nodes have no child summaries"),
        }
    }

    fn child_trees(&self) -> &SmallVec<[Tree<T>; 2 * TREE_BASE]> {
        match self {
            Node::Internal { child_trees, .. } => child_trees,
            Node::Leaf { .. } => panic!("Leaf nodes have no child trees"),
        }
    }

    fn items(&self) -> &SmallVec<[T; 2 * TREE_BASE]> {
        match self {
            Node::Leaf { items, .. } => items,
            Node::Internal { .. } => panic!("Internal nodes have no items"),
        }
    }

    fn items_mut(&mut self) -> &mut SmallVec<[T; 2 * TREE_BASE]> {
        match self {
            Node::Leaf { items, .. } => items,
            Node::Internal { .. } => panic!("Internal nodes have no items"),
        }
    }

    fn summary_mut(&mut self) -> &mut T::Summary {
        match self {
            Node::Internal { summary, .. } => summary,
            Node::Leaf { summary, .. } => summary,
        }
    }

    fn is_underflowing(&self) -> bool {
        match self {
            Node::Internal { child_trees, .. } => child_trees.len() < TREE_BASE,
            Node::Leaf { items, .. } => items.len() < TREE_BASE,
        }
    }
}

impl<T: Item> Clone for Node<T> {
    fn clone(&self) -> Self {
        match self {
            Node::Internal {
                height,
                summary,
                child_summaries,
                child_trees,
                ..
            } => Node::Internal {
                height: *height,
                summary: summary.clone(),
                child_summaries: child_summaries.clone(),
                child_trees: child_trees.clone(),
            },
            Node::Leaf { summary, items, .. } => Node::Leaf {
                summary: summary.clone(),
                items: items.clone(),
            },
        }
    }
}

impl<T: Item> Cursor<T> {
    fn new(tree: Tree<T>) -> Self {
        Self {
            tree,
            stack: SmallVec::new(),
            summary: T::Summary::default(),
            did_seek: false,
            at_end: false,
        }
    }

    fn reset(&mut self) {
        self.did_seek = false;
        self.at_end = false;
        self.stack.truncate(0);
        self.summary = T::Summary::default();
    }

    pub fn start<D: Dimension<T::Summary>>(&self) -> D {
        D::from_summary(&self.summary).clone()
    }

    pub fn end<D: Dimension<T::Summary>>(&self) -> D {
        if let Some(item) = self.item() {
            self.start::<D>() + &D::from_summary(&item.summarize())
        } else {
            self.start::<D>()
        }
    }

    pub fn item(&self) -> Option<T> {
        assert!(self.did_seek, "Must seek before calling this method");
        if let Some((subtree, index, _)) = self.stack.last() {
            match *subtree.0 {
                Node::Leaf { ref items, .. } => {
                    if *index == items.len() {
                        None
                    } else {
                        Some(items[*index].clone())
                    }
                }
                _ => unreachable!(),
            }
        } else {
            None
        }
    }

    pub fn prev_item(&self) -> Option<T> {
        assert!(self.did_seek, "Must seek before calling this method");
        if let Some((cur_leaf, index, _)) = self.stack.last() {
            if *index == 0 {
                if let Some(prev_leaf) = self.prev_leaf() {
                    let prev_leaf = prev_leaf.0;
                    Some(prev_leaf.items().last().unwrap().clone())
                } else {
                    None
                }
            } else {
                match *cur_leaf.0 {
                    Node::Leaf { ref items, .. } => Some(items[index - 1].clone()),
                    _ => unreachable!(),
                }
            }
        } else if self.at_end {
            self.tree.last()
        } else {
            None
        }
    }

    fn prev_leaf(&self) -> Option<Tree<T>> {
        for (ancestor, index, _) in self.stack.iter().rev().skip(1) {
            if *index != 0 {
                match *ancestor.0 {
                    Node::Internal {
                        ref child_trees, ..
                    } => return Some(child_trees[index - 1].rightmost_leaf()),
                    Node::Leaf { .. } => unreachable!(),
                };
            }
        }
        None
    }

    pub fn prev(&mut self) {
        assert!(self.did_seek, "Must seek before calling this method");

        if self.at_end {
            self.summary = T::Summary::default();
            let root = self.tree.clone();
            self.descend_to_last_item(root);
            self.at_end = false;
        } else {
            while let Some((subtree, index, _)) = self.stack.pop() {
                if index > 0 {
                    let new_index = index - 1;

                    self.summary = self
                        .stack
                        .last()
                        .map_or(T::Summary::default(), |(_, _, summary)| summary.clone());

                    match subtree.0.as_ref() {
                        Node::Internal {
                            child_trees,
                            child_summaries,
                            ..
                        } => {
                            for summary in &child_summaries[0..new_index] {
                                self.summary += summary;
                            }
                            self.stack
                                .push((subtree.clone(), new_index, self.summary.clone()));
                            self.descend_to_last_item(child_trees[new_index].clone());
                        }
                        Node::Leaf { items, .. } => {
                            for item in &items[0..new_index] {
                                self.summary += &item.summarize();
                            }
                            self.stack
                                .push((subtree.clone(), new_index, self.summary.clone()));
                        }
                    }

                    break;
                }
            }
        }
    }

    pub fn next(&mut self) {
        self.next_internal(|_| true)
    }

    fn next_internal<F>(&mut self, filter_node: F)
    where
        F: Fn(&T::Summary) -> bool,
    {
        assert!(self.did_seek, "Must seek before calling this method");

        if self.stack.is_empty() {
            if !self.at_end {
                let root = self.tree.clone();
                self.descend_to_first_item(root, filter_node);
            }
        } else {
            while self.stack.len() > 0 {
                let new_subtree = {
                    let (subtree, index, summary) = self.stack.last_mut().unwrap();
                    match subtree.0.as_ref() {
                        Node::Internal {
                            child_trees,
                            child_summaries,
                            ..
                        } => {
                            while *index < child_summaries.len() {
                                *summary += &child_summaries[*index];
                                *index += 1;
                                if let Some(next_summary) = child_summaries.get(*index) {
                                    if filter_node(next_summary) {
                                        break;
                                    } else {
                                        self.summary += next_summary;
                                    }
                                }
                            }

                            child_trees.get(*index).cloned()
                        }
                        Node::Leaf { items, .. } => loop {
                            let item_summary = items[*index].summarize();
                            self.summary += &item_summary;
                            *summary += &item_summary;
                            *index += 1;
                            if let Some(next_item) = items.get(*index) {
                                if filter_node(&next_item.summarize()) {
                                    return;
                                }
                            } else {
                                break None;
                            }
                        },
                    }
                };

                if let Some(subtree) = new_subtree {
                    self.descend_to_first_item(subtree, filter_node);
                    break;
                } else {
                    self.stack.pop();
                }
            }
        }

        self.at_end = self.stack.is_empty();
    }

    fn descend_to_first_item<F>(&mut self, mut subtree: Tree<T>, filter_node: F)
    where
        F: Fn(&T::Summary) -> bool,
    {
        self.did_seek = true;
        loop {
            subtree = match *subtree.0 {
                Node::Internal {
                    ref child_trees,
                    ref child_summaries,
                    ..
                } => {
                    let mut new_index = None;
                    for (index, summary) in child_summaries.iter().enumerate() {
                        if filter_node(summary) {
                            new_index = Some(index);
                            break;
                        }
                        self.summary += summary;
                    }

                    if let Some(new_index) = new_index {
                        self.stack
                            .push((subtree.clone(), new_index, self.summary.clone()));
                        child_trees[new_index].clone()
                    } else {
                        break;
                    }
                }
                Node::Leaf { ref items, .. } => {
                    let mut new_index = None;
                    for (index, item) in items.iter().enumerate() {
                        let summary = item.summarize();
                        if filter_node(&summary) {
                            new_index = Some(index);
                            break;
                        }
                        self.summary += &summary;
                    }

                    if let Some(new_index) = new_index {
                        self.stack
                            .push((subtree.clone(), new_index, self.summary.clone()));
                    }
                    break;
                }
            }
        }
    }

    fn descend_to_last_item(&mut self, mut subtree: Tree<T>) {
        self.did_seek = true;
        loop {
            match subtree.0.clone().as_ref() {
                Node::Internal {
                    child_trees,
                    child_summaries,
                    ..
                } => {
                    for summary in &child_summaries[0..child_summaries.len() - 1] {
                        self.summary += summary;
                    }
                    self.stack
                        .push((subtree.clone(), child_trees.len() - 1, self.summary.clone()));
                    subtree = child_trees.last().unwrap().clone();
                }
                Node::Leaf { items, .. } => {
                    let last_index = items.len().saturating_sub(1);
                    for item in &items[0..last_index] {
                        self.summary += &item.summarize();
                    }
                    self.stack
                        .push((subtree.clone(), last_index, self.summary.clone()));
                    break;
                }
            }
        }
    }

    pub fn seek<D>(&mut self, pos: &D, bias: SeekBias) -> bool
    where
        D: Dimension<T::Summary>,
    {
        self.reset();
        self.seek_internal(pos, bias, None)
    }

    pub fn seek_forward<D>(&mut self, pos: &D, bias: SeekBias) -> bool
    where
        D: Dimension<T::Summary>,
    {
        self.seek_internal(pos, bias, None)
    }

    pub fn slice<D>(&mut self, end: &D, bias: SeekBias) -> Tree<T>
    where
        D: Dimension<T::Summary>,
    {
        let mut slice = Tree::new();
        self.seek_internal(end, bias, Some(&mut slice));
        slice
    }

    pub fn suffix<D>(&mut self) -> Tree<T>
    where
        D: Dimension<T::Summary>,
    {
        let extent = self.tree.extent::<D>();
        let mut slice = Tree::new();
        self.seek_internal(&extent, SeekBias::Right, Some(&mut slice));
        slice
    }

    fn seek_internal<D>(
        &mut self,
        target: &D,
        bias: SeekBias,
        mut slice: Option<&mut Tree<T>>,
    ) -> bool
    where
        D: Dimension<T::Summary>,
    {
        let mut pos = D::from_summary(&self.summary).clone();
        debug_assert!(target >= &pos);
        let mut containing_subtree = None;

        if self.did_seek {
            'outer: while self.stack.len() > 0 {
                {
                    let (parent_subtree, index, _) = self.stack.last_mut().unwrap();
                    match *parent_subtree.0 {
                        Node::Internal {
                            ref child_summaries,
                            ref child_trees,
                            ..
                        } => {
                            *index += 1;
                            while *index < child_summaries.len() {
                                let child_tree = &child_trees[*index];
                                let child_summary = &child_summaries[*index];
                                let mut child_end = pos;
                                child_end += &D::from_summary(&child_summary);

                                let comparison = target.cmp(&child_end);
                                if comparison == Ordering::Greater
                                    || (comparison == Ordering::Equal && bias == SeekBias::Right)
                                {
                                    self.summary += child_summary;
                                    pos = child_end;
                                    if let Some(slice) = slice.as_mut() {
                                        slice.push_tree(child_tree.clone());
                                    }
                                    *index += 1;
                                } else {
                                    pos = D::from_summary(&self.summary).clone();
                                    containing_subtree = Some(child_tree.clone());
                                    break 'outer;
                                }
                            }
                        }
                        Node::Leaf { ref items, .. } => {
                            let mut slice_items = SmallVec::<[T; 2 * TREE_BASE]>::new();
                            let mut slice_items_summary = T::Summary::default();

                            while *index < items.len() {
                                let item = &items[*index];
                                let item_summary = item.summarize();
                                let mut item_end = pos;
                                item_end += &D::from_summary(&item_summary);

                                let comparison = target.cmp(&item_end);
                                if comparison == Ordering::Greater
                                    || (comparison == Ordering::Equal && bias == SeekBias::Right)
                                {
                                    self.summary += &item_summary;
                                    pos = item_end;
                                    if slice.is_some() {
                                        slice_items.push(item.clone());
                                        slice_items_summary += &item_summary;
                                    }
                                    *index += 1;
                                } else {
                                    pos = D::from_summary(&self.summary).clone();
                                    if let Some(slice) = slice.as_mut() {
                                        slice.push_tree(Tree(Arc::new(Node::Leaf {
                                            summary: slice_items_summary,
                                            items: slice_items,
                                        })));
                                    }
                                    break 'outer;
                                }
                            }

                            if let Some(slice) = slice.as_mut() {
                                if slice_items.len() > 0 {
                                    slice.push_tree(Tree(Arc::new(Node::Leaf {
                                        summary: slice_items_summary,
                                        items: slice_items,
                                    })));
                                }
                            }
                        }
                    }
                }

                self.stack.pop();
            }
        } else {
            self.did_seek = true;
            containing_subtree = Some(self.tree.clone());
        }

        if let Some(mut subtree) = containing_subtree {
            loop {
                let mut next_subtree = None;
                match *subtree.0 {
                    Node::Internal {
                        ref child_summaries,
                        ref child_trees,
                        ..
                    } => {
                        for (index, child_summary) in child_summaries.iter().enumerate() {
                            let mut child_end = pos;
                            child_end += &D::from_summary(child_summary);

                            let comparison = target.cmp(&child_end);
                            if comparison == Ordering::Greater
                                || (comparison == Ordering::Equal && bias == SeekBias::Right)
                            {
                                self.summary += child_summary;
                                pos = child_end;
                                if let Some(slice) = slice.as_mut() {
                                    slice.push_tree(child_trees[index].clone());
                                }
                            } else {
                                pos = D::from_summary(&self.summary).clone();
                                self.stack
                                    .push((subtree.clone(), index, self.summary.clone()));
                                next_subtree = Some(child_trees[index].clone());
                                break;
                            }
                        }
                    }
                    Node::Leaf { ref items, .. } => {
                        let mut slice_items = SmallVec::<[T; 2 * TREE_BASE]>::new();
                        let mut slice_items_summary = T::Summary::default();

                        for (index, item) in items.iter().enumerate() {
                            let item_summary = item.summarize();
                            let mut child_end = pos;
                            child_end += &D::from_summary(&item_summary);

                            let comparison = target.cmp(&child_end);
                            if comparison == Ordering::Greater
                                || (comparison == Ordering::Equal && bias == SeekBias::Right)
                            {
                                if slice.is_some() {
                                    slice_items.push(item.clone());
                                    slice_items_summary += &item_summary;
                                }
                                self.summary += &item_summary;
                                pos = child_end;
                            } else {
                                pos = D::from_summary(&self.summary).clone();
                                self.stack
                                    .push((subtree.clone(), index, self.summary.clone()));
                                break;
                            }
                        }

                        if let Some(slice) = slice.as_mut() {
                            if slice_items.len() > 0 {
                                slice.push_tree(Tree(Arc::new(Node::Leaf {
                                    summary: slice_items_summary,
                                    items: slice_items,
                                })));
                            }
                        }
                    }
                };

                if let Some(next_subtree) = next_subtree {
                    subtree = next_subtree;
                } else {
                    break;
                }
            }
        }

        self.at_end = self.stack.is_empty();
        if bias == SeekBias::Left {
            *target == self.end::<D>()
        } else {
            *target == self.start::<D>()
        }
    }
}

impl<T: Item> Iterator for Cursor<T> {
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        if !self.did_seek {
            let root = self.tree.clone();
            self.descend_to_first_item(root, |_| true);
        }

        if let Some(item) = self.item() {
            self.next();
            Some(item)
        } else {
            None
        }
    }
}

impl<F: Fn(&T::Summary) -> bool, T: Item> FilterCursor<F, T> {
    fn new(tree: &Tree<T>, filter_node: F) -> Self {
        let mut cursor = tree.cursor();
        if filter_node(&tree.summary()) {
            cursor.descend_to_first_item(tree.clone(), &filter_node);
        } else {
            cursor.did_seek = true;
            cursor.at_end = true;
        }

        Self {
            cursor,
            filter_node,
        }
    }

    pub fn start<D: Dimension<T::Summary>>(&self) -> D {
        self.cursor.start()
    }

    pub fn item(&self) -> Option<T> {
        self.cursor.item()
    }

    pub fn next(&mut self) {
        self.cursor.next_internal(&self.filter_node);
    }
}

impl<F: Fn(&T::Summary) -> bool, T: Item> Iterator for FilterCursor<F, T> {
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(item) = self.item() {
            self.cursor.next_internal(&self.filter_node);
            Some(item)
        } else {
            None
        }
    }
}

impl<T: KeyedItem> Edit<T> {
    fn key(&self) -> T::Key {
        match self {
            Edit::Insert(item) | Edit::Remove(item) => item.key(),
        }
    }
}

fn sum<'a, T, I>(iter: I) -> T
where
    T: 'a + Default + AddAssign<&'a T>,
    I: Iterator<Item = &'a T>,
{
    let mut sum = T::default();
    for value in iter {
        sum += value;
    }
    sum
}

fn sum_owned<T, I>(iter: I) -> T
where
    T: Default + for<'a> AddAssign<&'a T>,
    I: Iterator<Item = T>,
{
    let mut sum = T::default();
    for value in iter {
        sum += &value;
    }
    sum
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_extend_and_push_tree() {
        let mut tree1 = Tree::new();
        tree1.extend(0..20);

        let mut tree2 = Tree::new();
        tree2.extend(50..100);

        tree1.push_tree(tree2);
        assert_eq!(tree1.items(), (0..20).chain(50..100).collect::<Vec<u8>>());
    }

    #[test]
    fn test_random() {
        for seed in 0..100 {
            use rand::{Rng, SeedableRng, StdRng};

            let mut rng = StdRng::from_seed(&[seed]);

            let mut tree = Tree::<u8>::new();
            let count = rng.gen_range(0, 10);
            tree.extend(rng.gen_iter().take(count));

            for _ in 0..5 {
                let splice_end = rng.gen_range(0, tree.extent::<Count>().0 + 1);
                let splice_start = rng.gen_range(0, splice_end + 1);
                let count = rng.gen_range(0, 3);
                let tree_end = tree.extent::<Count>();
                let new_items = rng.gen_iter().take(count).collect::<Vec<u8>>();

                let mut reference_items = tree.items();
                reference_items.splice(splice_start..splice_end, new_items.clone());

                let mut cursor = tree.cursor();
                tree = cursor.slice(&Count(splice_start), SeekBias::Right);
                tree.extend(new_items);
                cursor.seek(&Count(splice_end), SeekBias::Right);
                tree.push_tree(cursor.slice(&tree_end, SeekBias::Right));

                assert_eq!(tree.items(), reference_items);

                let mut filter_cursor = tree.filter(|summary| summary.contains_even);
                let mut reference_filter = tree
                    .items()
                    .into_iter()
                    .enumerate()
                    .filter(|(_, item)| (item & 1) == 0);
                while let Some(actual_item) = filter_cursor.item() {
                    let (reference_index, reference_item) = reference_filter.next().unwrap();
                    assert_eq!(actual_item, reference_item);
                    assert_eq!(filter_cursor.start::<Count>().0, reference_index);
                    filter_cursor.next();
                }
                assert!(reference_filter.next().is_none());

                let mut pos = rng.gen_range(0, tree.extent::<Count>().0 + 1);
                let mut before_start = false;
                let mut cursor = tree.cursor();
                cursor.seek(&Count(pos), SeekBias::Right);

                for i in 0..10 {
                    assert_eq!(cursor.start::<Count>().0, pos);

                    if pos > 0 {
                        assert_eq!(cursor.prev_item().unwrap(), reference_items[pos - 1]);
                    } else {
                        assert_eq!(cursor.prev_item(), None);
                    }

                    if pos < reference_items.len() && !before_start {
                        assert_eq!(cursor.item().unwrap(), reference_items[pos]);
                    } else {
                        assert_eq!(cursor.item(), None);
                    }

                    if i < 5 {
                        cursor.next();
                        if pos < reference_items.len() {
                            pos += 1;
                            before_start = false;
                        }
                    } else {
                        cursor.prev();
                        if pos == 0 {
                            before_start = true;
                        }
                        pos = pos.saturating_sub(1);
                    }
                }
            }
        }
    }

    #[test]
    fn test_cursor() {
        // Empty tree
        let tree = Tree::<u8>::new();
        let mut cursor = tree.cursor();
        assert_eq!(cursor.slice(&Sum(0), SeekBias::Right).items(), vec![]);
        assert_eq!(cursor.item(), None);
        assert_eq!(cursor.prev_item(), None);
        assert_eq!(cursor.start::<Count>(), Count(0));
        assert_eq
Download .txt
gitextract_hfrilqj1/

├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── Cargo.toml
├── LICENSE
├── README.md
├── docs/
│   ├── architecture/
│   │   ├── 001_client_server_protocol.md
│   │   ├── 002_shared_workspaces.md
│   │   └── 003_memo_epochs.md
│   └── updates/
│       ├── 2018_03_05.md
│       ├── 2018_03_12.md
│       ├── 2018_03_19.md
│       ├── 2018_03_26.md
│       ├── 2018_04_02.md
│       ├── 2018_04_09.md
│       ├── 2018_04_16.md
│       ├── 2018_04_23.md
│       ├── 2018_04_30.md
│       ├── 2018_05_07.md
│       ├── 2018_05_14.md
│       ├── 2018_05_28.md
│       ├── 2018_07_10.md
│       ├── 2018_07_16.md
│       ├── 2018_07_23.md
│       ├── 2018_07_31.md
│       ├── 2018_08_21.md
│       ├── 2018_08_28.md
│       ├── 2018_09_14.md
│       └── 2018_10_02.md
├── memo_core/
│   ├── Cargo.toml
│   ├── README.md
│   ├── rustfmt.toml
│   ├── script/
│   │   └── compile_flatbuffers
│   └── src/
│       ├── btree.rs
│       ├── buffer.rs
│       ├── epoch.rs
│       ├── lib.rs
│       ├── operation_queue.rs
│       ├── serialization/
│       │   ├── mod.rs
│       │   ├── schema.fbs
│       │   └── schema_generated.rs
│       ├── time.rs
│       └── work_tree.rs
├── memo_js/
│   ├── .npmignore
│   ├── .nvmrc
│   ├── Cargo.toml
│   ├── README.md
│   ├── package.json
│   ├── rustfmt.toml
│   ├── script/
│   │   └── build
│   ├── src/
│   │   ├── index.ts
│   │   ├── lib.rs
│   │   └── support.ts
│   ├── test/
│   │   ├── tests.ts
│   │   └── tsconfig.json
│   ├── tsconfig.json
│   └── webpack.config.js
├── rust-toolchain
├── script/
│   ├── bench
│   ├── build
│   ├── cibuild
│   └── test
├── xray_browser/
│   ├── README.md
│   ├── package.json
│   ├── script/
│   │   ├── build
│   │   └── server
│   ├── src/
│   │   ├── client.js
│   │   ├── ui.js
│   │   └── worker.js
│   └── static/
│       └── index.html
├── xray_cli/
│   ├── Cargo.toml
│   ├── README.md
│   └── src/
│       └── main.rs
├── xray_core/
│   ├── Cargo.toml
│   ├── README.md
│   ├── benches/
│   │   └── bench.rs
│   └── src/
│       ├── app.rs
│       ├── buffer.rs
│       ├── buffer_view.rs
│       ├── cross_platform.rs
│       ├── file_finder.rs
│       ├── fs.rs
│       ├── fuzzy.rs
│       ├── lib.rs
│       ├── movement.rs
│       ├── never.rs
│       ├── notify_cell.rs
│       ├── project.rs
│       ├── rpc/
│       │   ├── client.rs
│       │   ├── messages.rs
│       │   ├── mod.rs
│       │   └── server.rs
│       ├── stream_ext.rs
│       ├── tree.rs
│       ├── wasm_logging.rs
│       ├── window.rs
│       └── workspace.rs
├── xray_electron/
│   ├── .gitignore
│   ├── README.md
│   ├── index.html
│   ├── lib/
│   │   ├── main_process/
│   │   │   └── main.js
│   │   ├── render_process/
│   │   │   └── main.js
│   │   └── shared/
│   │       └── xray_client.js
│   └── package.json
├── xray_server/
│   ├── Cargo.toml
│   ├── README.md
│   └── src/
│       ├── fs.rs
│       ├── json_lines_codec.rs
│       ├── main.rs
│       ├── messages.rs
│       └── server.rs
├── xray_ui/
│   ├── README.md
│   ├── lib/
│   │   ├── action_dispatcher.js
│   │   ├── app.js
│   │   ├── debounce.js
│   │   ├── file_finder.js
│   │   ├── index.js
│   │   ├── modal.js
│   │   ├── text_editor/
│   │   │   ├── shaders.js
│   │   │   ├── text_editor.js
│   │   │   └── text_plane.js
│   │   ├── theme_provider.js
│   │   ├── view.js
│   │   ├── view_registry.js
│   │   └── workspace.js
│   ├── package.json
│   └── test/
│       ├── action_dispatcher.test.js
│       ├── file_finder.test.js
│       ├── helpers/
│       │   └── component_helpers.js
│       ├── modal.test.js
│       ├── view.test.js
│       └── view_registry.test.js
└── xray_wasm/
    ├── .gitignore
    ├── Cargo.toml
    ├── lib/
    │   ├── main.js
    │   └── support.js
    ├── package.json
    ├── script/
    │   ├── build
    │   └── test
    ├── src/
    │   └── lib.rs
    └── test/
        └── tests.js
Download .txt
SYMBOL INDEX (2280 symbols across 62 files)

FILE: memo_core/src/btree.rs
  constant TREE_BASE (line 8) | const TREE_BASE: usize = 2;
  constant TREE_BASE (line 10) | const TREE_BASE: usize = 16;
  type Item (line 12) | pub trait Item: Clone + Eq + fmt::Debug {
    method summarize (line 15) | fn summarize(&self) -> Self::Summary;
    type Summary (line 1342) | type Summary = IntegersSummary;
    method summarize (line 1344) | fn summarize(&self) -> Self::Summary {
  type KeyedItem (line 18) | pub trait KeyedItem: Item {
    method key (line 21) | fn key(&self) -> Self::Key;
  type Dimension (line 24) | pub trait Dimension<Summary: Default>:
    method from_summary (line 27) | fn from_summary(summary: &Summary) -> Self;
    method default (line 29) | fn default() -> Self {
  type Tree (line 35) | pub struct Tree<T: Item>(Arc<Node<T>>);
  type Node (line 38) | pub enum Node<T: Item> {
  type Cursor (line 52) | pub struct Cursor<T: Item> {
  type FilterCursor (line 60) | pub struct FilterCursor<F: Fn(&T::Summary) -> bool, T: Item> {
  type SeekBias (line 66) | pub enum SeekBias {
  type Edit (line 72) | pub enum Edit<T: KeyedItem> {
  function new (line 78) | pub fn new() -> Self {
  function from_item (line 85) | pub fn from_item(item: T) -> Self {
  function items (line 92) | pub fn items(&self) -> Vec<T> {
  function cursor (line 107) | pub fn cursor(&self) -> Cursor<T> {
  function filter (line 111) | pub fn filter<F>(&self, filter_node: F) -> FilterCursor<F, T>
  function first (line 119) | pub fn first(&self) -> Option<T> {
  function last (line 123) | pub fn last(&self) -> Option<T> {
  function extent (line 127) | pub fn extent<D: Dimension<T::Summary>>(&self) -> D {
  function summary (line 134) | pub fn summary(&self) -> T::Summary {
  function is_empty (line 142) | pub fn is_empty(&self) -> bool {
  function extend (line 149) | pub fn extend<I>(&mut self, iter: I)
  function push (line 177) | pub fn push(&mut self, item: T) {
  function push_tree (line 184) | pub fn push_tree(&mut self, other: Self) {
  function push_tree_recursive (line 197) | fn push_tree_recursive(&mut self, other: Tree<T>) -> Option<Tree<T>> {
  function from_child_trees (line 294) | fn from_child_trees(child_trees: Vec<Tree<T>>) -> Self {
  function leftmost_leaf (line 309) | fn leftmost_leaf(&self) -> Tree<T> {
  function rightmost_leaf (line 318) | fn rightmost_leaf(&self) -> Tree<T> {
  function insert (line 329) | pub fn insert(&mut self, item: T) {
  function edit (line 337) | pub fn edit(&mut self, edits: &mut [Edit<T>]) {
  function is_leaf (line 381) | fn is_leaf(&self) -> bool {
  function height (line 388) | fn height(&self) -> u8 {
  function summary (line 395) | fn summary(&self) -> &T::Summary {
  function child_summaries (line 402) | fn child_summaries(&self) -> &[T::Summary] {
  function child_trees (line 411) | fn child_trees(&self) -> &SmallVec<[Tree<T>; 2 * TREE_BASE]> {
  function items (line 418) | fn items(&self) -> &SmallVec<[T; 2 * TREE_BASE]> {
  function items_mut (line 425) | fn items_mut(&mut self) -> &mut SmallVec<[T; 2 * TREE_BASE]> {
  function summary_mut (line 432) | fn summary_mut(&mut self) -> &mut T::Summary {
  function is_underflowing (line 439) | fn is_underflowing(&self) -> bool {
  method clone (line 448) | fn clone(&self) -> Self {
  function new (line 471) | fn new(tree: Tree<T>) -> Self {
  function reset (line 481) | fn reset(&mut self) {
  function start (line 488) | pub fn start<D: Dimension<T::Summary>>(&self) -> D {
  function end (line 492) | pub fn end<D: Dimension<T::Summary>>(&self) -> D {
  function item (line 500) | pub fn item(&self) -> Option<T> {
  function prev_item (line 518) | pub fn prev_item(&self) -> Option<T> {
  function prev_leaf (line 541) | fn prev_leaf(&self) -> Option<Tree<T>> {
  function prev (line 555) | pub fn prev(&mut self) {
  function next (line 601) | pub fn next(&mut self) {
  function next_internal (line 605) | fn next_internal<F>(&mut self, filter_node: F)
  function descend_to_first_item (line 668) | fn descend_to_first_item<F>(&mut self, mut subtree: Tree<T>, filter_node...
  function descend_to_last_item (line 718) | fn descend_to_last_item(&mut self, mut subtree: Tree<T>) {
  function seek (line 747) | pub fn seek<D>(&mut self, pos: &D, bias: SeekBias) -> bool
  function seek_forward (line 755) | pub fn seek_forward<D>(&mut self, pos: &D, bias: SeekBias) -> bool
  function slice (line 762) | pub fn slice<D>(&mut self, end: &D, bias: SeekBias) -> Tree<T>
  function suffix (line 771) | pub fn suffix<D>(&mut self) -> Tree<T>
  function seek_internal (line 781) | fn seek_internal<D>(
  type Item (line 967) | type Item = T;
  method next (line 969) | fn next(&mut self) -> Option<Self::Item> {
  function new (line 985) | fn new(tree: &Tree<T>, filter_node: F) -> Self {
  function start (line 1000) | pub fn start<D: Dimension<T::Summary>>(&self) -> D {
  function item (line 1004) | pub fn item(&self) -> Option<T> {
  function next (line 1008) | pub fn next(&mut self) {
  type Item (line 1014) | type Item = T;
  method next (line 1016) | fn next(&mut self) -> Option<Self::Item> {
  function key (line 1027) | fn key(&self) -> T::Key {
  function sum (line 1034) | fn sum<'a, T, I>(iter: I) -> T
  function sum_owned (line 1046) | fn sum_owned<T, I>(iter: I) -> T
  function test_extend_and_push_tree (line 1063) | fn test_extend_and_push_tree() {
  function test_random (line 1075) | fn test_random() {
  function test_cursor (line 1156) | fn test_cursor() {
  type IntegersSummary (line 1329) | pub struct IntegersSummary {
    method add_assign (line 1354) | fn add_assign(&mut self, other: &Self) {
  type Count (line 1336) | struct Count(usize);
    method from_summary (line 1362) | fn from_summary(summary: &IntegersSummary) -> Self {
    method add_assign (line 1368) | fn add_assign(&mut self, other: &Self) {
    type Output (line 1374) | type Output = Self;
    method add (line 1376) | fn add(mut self, other: &Self) -> Self {
  type Sum (line 1339) | struct Sum(usize);
    method from_summary (line 1383) | fn from_summary(summary: &IntegersSummary) -> Self {
    method add_assign (line 1389) | fn add_assign(&mut self, other: &Self) {
    type Output (line 1395) | type Output = Self;
    method add (line 1397) | fn add(mut self, other: &Self) -> Self {

FILE: memo_core/src/buffer.rs
  type SelectionSetId (line 19) | pub type SelectionSetId = time::Lamport;
  type SelectionsVersion (line 20) | pub type SelectionsVersion = usize;
  type Buffer (line 23) | pub struct Buffer {
    method new (line 172) | pub fn new<T>(base_text: T) -> Self
    method is_modified (line 236) | pub fn is_modified(&self) -> bool {
    method len (line 240) | pub fn len(&self) -> usize {
    method len_for_row (line 244) | pub fn len_for_row(&self, row: u32) -> Result<u32, Error> {
    method longest_row (line 255) | pub fn longest_row(&self) -> u32 {
    method max_point (line 259) | pub fn max_point(&self) -> Point {
    method line (line 263) | pub fn line(&self, row: u32) -> Result<Vec<u16>, Error> {
    method to_u16_chars (line 272) | pub fn to_u16_chars(&self) -> Vec<u16> {
    method to_string (line 276) | pub fn to_string(&self) -> String {
    method iter (line 280) | pub fn iter(&self) -> Iter {
    method iter_at_point (line 284) | pub fn iter_at_point(&self, point: Point) -> Iter {
    method selections_changed_since (line 288) | pub fn selections_changed_since(&self, since: SelectionsVersion) -> bo...
    method changes_since (line 292) | pub fn changes_since(&self, since: &time::Global) -> impl Iterator<Ite...
    method deferred_ops_len (line 303) | pub fn deferred_ops_len(&self) -> usize {
    method edit (line 307) | pub fn edit<I, T>(
    method edit_2d (line 349) | pub fn edit_2d<I, T>(
    method add_selection_set (line 371) | pub fn add_selection_set<I>(
    method replace_selection_set (line 395) | pub fn replace_selection_set<I>(
    method remove_selection_set (line 422) | pub fn remove_selection_set(
    method selection_ranges (line 439) | pub fn selection_ranges<'a>(
    method all_selections (line 458) | pub fn all_selections(&self) -> impl Iterator<Item = (&SelectionSetId,...
    method all_selection_ranges (line 462) | pub fn all_selection_ranges<'a>(
    method merge_selections (line 470) | fn merge_selections(&mut self, selections: &mut Vec<Selection>) {
    method selections_from_ranges (line 498) | fn selections_from_ranges<I>(&self, ranges: I) -> Result<Vec<Selection...
    method apply_ops (line 524) | pub fn apply_ops<I: IntoIterator<Item = Operation>>(
    method apply_op (line 544) | fn apply_op(
    method apply_edit (line 596) | fn apply_edit(
    method flush_deferred_ops (line 712) | fn flush_deferred_ops(
    method can_apply_op (line 731) | fn can_apply_op(&self, op: &Operation) -> bool {
    method resolve_fragment_id (line 771) | fn resolve_fragment_id(
    method splice_fragments (line 789) | fn splice_fragments<I>(
    method split_fragment (line 1033) | fn split_fragment(
    method build_fragment_to_insert (line 1118) | fn build_fragment_to_insert(
    method anchor_before_offset (line 1152) | pub fn anchor_before_offset(&self, offset: usize) -> Result<Anchor, Er...
    method anchor_after_offset (line 1156) | pub fn anchor_after_offset(&self, offset: usize) -> Result<Anchor, Err...
    method anchor_for_offset (line 1160) | fn anchor_for_offset(&self, offset: usize, bias: AnchorBias) -> Result...
    method anchor_before_point (line 1199) | pub fn anchor_before_point(&self, point: Point) -> Result<Anchor, Erro...
    method anchor_after_point (line 1203) | pub fn anchor_after_point(&self, point: Point) -> Result<Anchor, Error> {
    method anchor_for_point (line 1207) | fn anchor_for_point(&self, point: Point, bias: AnchorBias) -> Result<A...
    method offset_for_anchor (line 1246) | pub fn offset_for_anchor(&self, anchor: &Anchor) -> Result<usize, Erro...
    method point_for_anchor (line 1250) | pub fn point_for_anchor(&self, anchor: &Anchor) -> Result<Point, Error> {
    method position_for_anchor (line 1254) | fn position_for_anchor(&self, anchor: &Anchor) -> Result<(usize, Point...
    method offset_for_point (line 1313) | fn offset_for_point(&self, point: Point) -> Result<usize, Error> {
    method cmp_anchors (line 1340) | pub fn cmp_anchors(&self, a: &Anchor, b: &Anchor) -> Result<Ordering, ...
    method cache_position (line 1346) | fn cache_position(&self, anchor: Option<Anchor>, offset: usize, point:...
    method randomly_mutate (line 3068) | pub fn randomly_mutate<T>(
    method point_for_offset (line 3136) | fn point_for_offset(&self, offset: usize) -> Result<Point, Error> {
  type Point (line 37) | pub struct Point {
    method new (line 1360) | pub fn new(row: u32, column: u32) -> Self {
    method zero (line 1364) | pub fn zero() -> Self {
    method is_zero (line 1368) | pub fn is_zero(&self) -> bool {
    method from_summary (line 1374) | fn from_summary(summary: &FragmentSummary) -> Self {
    type Output (line 1380) | type Output = Point;
    method add (line 1382) | fn add(self, other: &'a Self) -> Self::Output {
    type Output (line 1392) | type Output = Point;
    method sub (line 1394) | fn sub(self, other: &'a Self) -> Self::Output {
    method add_assign (line 1406) | fn add_assign(&mut self, other: &'a Self) {
  type Anchor (line 43) | pub enum Anchor {
    method to_flatbuf (line 1440) | fn to_flatbuf<'fbb>(
    method from_flatbuf (line 1475) | fn from_flatbuf<'fbb>(
  type AnchorBias (line 54) | pub enum AnchorBias {
    method to_flatbuf (line 1495) | fn to_flatbuf(&self) -> serialization::buffer::AnchorBias {
    method from_flatbuf (line 1502) | fn from_flatbuf(message: serialization::buffer::AnchorBias) -> Self {
  type Selection (line 60) | pub struct Selection {
    method head (line 1712) | pub fn head(&self) -> &Anchor {
    method set_head (line 1720) | pub fn set_head<S>(&mut self, buffer: &Buffer, cursor: Anchor) {
    method tail (line 1736) | pub fn tail(&self) -> &Anchor {
    method is_empty (line 1744) | pub fn is_empty(&self, buffer: &Buffer) -> bool {
    method anchor_range (line 1748) | pub fn anchor_range(&self) -> Range<Anchor> {
    method to_flatbuf (line 1752) | fn to_flatbuf<'fbb>(
    method from_flatbuf (line 1769) | fn from_flatbuf<'fbb>(
  type Iter (line 66) | pub struct Iter {
    method new (line 1511) | fn new(buffer: &Buffer) -> Self {
    method at_point (line 1521) | fn at_point(buffer: &Buffer, point: Point) -> Self {
    method rev (line 1538) | pub fn rev(mut self) -> Iter {
    method into_string (line 1543) | pub fn into_string(self) -> String {
  type ChangesIter (line 72) | struct ChangesIter<F: Fn(&FragmentSummary) -> bool> {
  type Change (line 78) | pub struct Change {
  type Insertion (line 85) | pub struct Insertion {
  type Text (line 94) | pub struct Text {
    method new (line 1781) | pub fn new(code_units: Vec<u16>) -> Self {
    method extent (line 1876) | fn extent(code_units: &[u16]) -> Point {
    method len (line 1890) | fn len(&self) -> usize {
    method longest_row_in_range (line 1894) | fn longest_row_in_range(&self, target_range: Range<usize>) -> Result<(...
    method point_for_offset (line 1969) | fn point_for_offset(&self, offset: usize) -> Result<Point, Error> {
    method offset_for_point (line 1986) | fn offset_for_point(&self, point: Point) -> Result<usize, Error> {
    method search (line 1998) | fn search<F>(&self, mut f: F) -> Option<(Range<usize>, u32, &LineNode)>
    method from (line 2041) | fn from(s: &'a str) -> Self {
    method from (line 2047) | fn from(s: String) -> Self {
    method from (line 2053) | fn from(s: Vec<u16>) -> Self {
  type LineNode (line 100) | struct LineNode {
  type LineNodeProbe (line 108) | struct LineNodeProbe<'a> {
  type FragmentId (line 119) | struct FragmentId(Arc<Vec<u16>>);
    method min_value (line 2069) | fn min_value() -> Self {
    method max_value (line 2073) | fn max_value() -> Self {
    method between (line 2077) | fn between(left: &Self, right: &Self) -> Self {
    method between_with_max (line 2081) | fn between_with_max(left: &Self, right: &Self, max_value: u16) -> Self {
    method from_summary (line 2101) | fn from_summary(summary: &FragmentSummary) -> Self {
    type Output (line 2107) | type Output = FragmentId;
    method add (line 2109) | fn add(self, other: &'a Self) -> Self::Output {
    method add_assign (line 2116) | fn add_assign(&mut self, other: &'a Self) {
  type Fragment (line 122) | struct Fragment {
    method new (line 2123) | fn new(id: FragmentId, insertion: Insertion) -> Self {
    method code_unit (line 2134) | fn code_unit(&self, offset: usize) -> Option<u16> {
    method code_units (line 2142) | fn code_units(&self) -> &[u16] {
    method len (line 2146) | fn len(&self) -> usize {
    method extent (line 2154) | fn extent(&self) -> usize {
    method extent_2d (line 2158) | fn extent_2d(&self) -> Point {
    method is_visible (line 2162) | fn is_visible(&self) -> bool {
    method was_visible (line 2166) | fn was_visible(&self, version: &time::Global) -> bool {
    method point_for_offset (line 2170) | fn point_for_offset(&self, offset: usize) -> Result<Point, Error> {
    method offset_for_point (line 2177) | fn offset_for_point(&self, point: Point) -> Result<usize, Error> {
    type Summary (line 2185) | type Summary = FragmentSummary;
    method summarize (line 2187) | fn summarize(&self) -> Self::Summary {
  type FragmentSummary (line 131) | pub struct FragmentSummary {
    method add_assign (line 2240) | fn add_assign(&mut self, other: &Self) {
  type InsertionSplit (line 142) | struct InsertionSplit {
    type Summary (line 2283) | type Summary = InsertionSplitSummary;
    method summarize (line 2285) | fn summarize(&self) -> Self::Summary {
  type InsertionSplitSummary (line 148) | struct InsertionSplitSummary {
    method add_assign (line 2293) | fn add_assign(&mut self, other: &Self) {
  type Operation (line 153) | pub enum Operation {
    method replica_id (line 2311) | fn replica_id(&self) -> ReplicaId {
    method lamport_timestamp (line 2315) | fn lamport_timestamp(&self) -> time::Lamport {
    method is_edit (line 2326) | pub fn is_edit(&self) -> bool {
    method to_flatbuf (line 2333) | pub fn to_flatbuf<'fbb>(
    method from_flatbuf (line 2404) | pub fn from_flatbuf<'fbb>(
    method timestamp (line 2472) | fn timestamp(&self) -> time::Lamport {
  method partial_cmp (line 1417) | fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
  method cmp (line 1424) | fn cmp(&self, other: &Point) -> Ordering {
  method cmp (line 1431) | fn cmp(&self, other: &Point) -> Ordering {
  type Item (line 1549) | type Item = u16;
  method next (line 1551) | fn next(&mut self) -> Option<Self::Item> {
  type Item (line 1601) | type Item = Change;
  method next (line 1603) | fn next(&mut self) -> Option<Self::Item> {
  function diff (line 1646) | pub fn diff(a: &[u16], b: &[u16]) -> Vec<Change> {
  function log2_fast (line 2059) | fn log2_fast(x: usize) -> usize {
  method default (line 2263) | fn default() -> Self {
  function from_summary (line 2277) | fn from_summary(summary: &FragmentSummary) -> Self {
  method default (line 2299) | fn default() -> Self {
  function from_summary (line 2305) | fn from_summary(summary: &InsertionSplitSummary) -> Self {
  function test_edit (line 2484) | fn test_edit() {
  function test_random_edits (line 2503) | fn test_random_edits() {
  function test_len_for_row (line 2550) | fn test_len_for_row() {
  function test_longest_row (line 2585) | fn test_longest_row() {
  function test_iter_starting_at_point (line 2614) | fn test_iter_starting_at_point() {
  function test_point_for_offset (line 2683) | fn test_point_for_offset() {
  function test_offset_for_point (line 2709) | fn test_offset_for_point() {
  function test_longest_row_in_range (line 2739) | fn test_longest_row_in_range() {
  function test_fragment_ids (line 2782) | fn test_fragment_ids() {
  function test_anchors (line 2803) | fn test_anchors() {
  function test_anchors_at_start_and_end (line 2948) | fn test_anchors_at_start_and_end() {
  function test_is_modified (line 2974) | fn test_is_modified() {
  function test_random_concurrent_edits (line 2986) | fn test_random_concurrent_edits() {
  type RandomCharIter (line 3053) | struct RandomCharIter<T: Rng>(T);
  type Item (line 3056) | type Item = char;
  method next (line 3058) | fn next(&mut self) -> Option<Self::Item> {

FILE: memo_core/src/epoch.rs
  constant ROOT_FILE_ID (line 20) | pub const ROOT_FILE_ID: FileId = FileId::Base(0);
  type Id (line 22) | pub type Id = time::Lamport;
  type Epoch (line 25) | pub struct Epoch {
    method new (line 189) | pub fn new(replica_id: ReplicaId, id: Id, head: Option<Oid>) -> Self {
    method buffer_version (line 206) | pub fn buffer_version(&self, file_id: FileId) -> Result<time::Global, ...
    method buffer_selections_last_update (line 214) | pub fn buffer_selections_last_update(
    method version (line 225) | pub fn version(&self) -> time::Global {
    method cursor (line 229) | pub fn cursor(&self) -> Option<Cursor> {
    method append_base_entries (line 248) | pub fn append_base_entries<I>(
    method apply_ops (line 320) | pub fn apply_ops<I>(
    method apply_ops_internal (line 335) | fn apply_ops_internal<I>(
    method apply_op (line 383) | pub fn apply_op(
    method can_apply_op (line 552) | fn can_apply_op(&self, op: &Operation) -> bool {
    method create_file (line 563) | pub fn create_file<N>(
    method new_text_file (line 597) | pub fn new_text_file(&mut self, lamport_clock: &mut time::Lamport) -> ...
    method open_text_file (line 610) | pub fn open_text_file<T>(
    method rename (line 641) | pub fn rename<N>(
    method remove (line 674) | pub fn remove(
    method set_active_location (line 691) | pub fn set_active_location(
    method replica_location (line 714) | pub fn replica_location(&self, replica_id: ReplicaId) -> Option<FileId> {
    method replica_locations (line 720) | pub fn replica_locations<'a>(&'a self) -> impl Iterator<Item = (Replic...
    method edit (line 728) | pub fn edit<I, T>(
    method edit_2d (line 748) | pub fn edit_2d<I, T>(
    method add_selection_set (line 768) | pub fn add_selection_set<I>(
    method replace_selection_set (line 790) | pub fn replace_selection_set<I>(
    method remove_selection_set (line 810) | pub fn remove_selection_set(
    method all_selections (line 826) | pub fn all_selections(
    method selection_ranges (line 840) | pub fn selection_ranges<'a>(
    method all_selection_ranges (line 852) | pub fn all_selection_ranges<'a>(
    method mutate_buffer (line 863) | fn mutate_buffer<F>(
    method file_id (line 891) | pub fn file_id<P>(&self, path: P) -> Result<FileId, Error>
    method base_path (line 928) | pub fn base_path(&self, mut file_id: FileId) -> Option<PathBuf> {
    method path (line 958) | pub fn path(&self, file_id: FileId) -> Option<PathBuf> {
    method text (line 971) | pub fn text(&self, file_id: FileId) -> Result<buffer::Iter, Error> {
    method selections_changed_since (line 979) | pub fn selections_changed_since(
    method changes_since (line 991) | pub fn changes_since(
    method buffer_deferred_ops_len (line 1003) | pub fn buffer_deferred_ops_len(&self, file_id: FileId) -> Result<usize...
    method file_type (line 1011) | pub fn file_type(&self, file_id: FileId) -> Result<FileType, Error> {
    method metadata (line 1015) | fn metadata(&self, file_id: FileId) -> Result<Metadata, Error> {
    method check_file_id (line 1031) | fn check_file_id(&self, file_id: FileId, expected_type: Option<FileTyp...
    method visit_ancestors (line 1042) | fn visit_ancestors<F>(&self, file_id: FileId, mut f: F) -> bool
    method fix_conflicts (line 1077) | fn fix_conflicts(
    method fix_name_conflicts (line 1189) | fn fix_name_conflicts(
    method with_replica_id (line 2520) | pub fn with_replica_id(replica_id: ReplicaId) -> Self {
    method entries (line 2524) | pub fn entries(&self) -> Vec<CursorEntry> {
    method dir_entries (line 2537) | pub fn dir_entries(&self) -> Vec<DirEntry> {
    method paths (line 2557) | fn paths(&self) -> Vec<String> {
    method randomly_mutate (line 2570) | pub fn randomly_mutate<T: Rng>(
    method select_file (line 2658) | fn select_file<T: Rng>(
  type Cursor (line 40) | pub struct Cursor<'a> {
  type CursorStackEntry (line 49) | struct CursorStackEntry {
  type CursorEntry (line 55) | pub struct CursorEntry {
  type DirEntry (line 65) | pub struct DirEntry {
    method from (line 2681) | fn from(entry: CursorEntry) -> Self {
  type Operation (line 77) | pub enum Operation {
    method local_timestamp (line 1379) | fn local_timestamp(&self) -> Option<time::Local> {
    method lamport_timestamp (line 1394) | pub fn lamport_timestamp(&self) -> time::Lamport {
    method to_flatbuf (line 1411) | pub fn to_flatbuf<'fbb>(
    method from_flatbuf (line 1556) | pub fn from_flatbuf<'a>(
    method timestamp (line 1657) | fn timestamp(&self) -> time::Lamport {
  type FileId (line 104) | pub enum FileId {
    method is_base (line 1663) | pub fn is_base(&self) -> bool {
    method to_flatbuf (line 1671) | fn to_flatbuf<'fbb>(
    method from_flatbuf (line 1697) | fn from_flatbuf<'a>(
    method from_summary (line 1732) | fn from_summary(summary: &Self) -> Self {
    method add_assign (line 1744) | fn add_assign(&mut self, other: &Self) {
    type Output (line 1751) | type Output = Self;
    method add (line 1753) | fn add(self, other: &Self) -> Self {
    method from_summary (line 1833) | fn from_summary(summary: &ParentRefValueKey) -> Self {
    method from_summary (line 1894) | fn from_summary(summary: &ChildRefValueSummary) -> Self {
  type FileStatus (line 110) | pub enum FileStatus {
  type FileType (line 120) | pub enum FileType {
    method to_flatbuf (line 1716) | fn to_flatbuf(&self) -> serialization::epoch::FileType {
    method from_flatbuf (line 1723) | fn from_flatbuf(message: &serialization::epoch::FileType) -> Self {
  type Metadata (line 126) | struct Metadata {
    type Summary (line 1760) | type Summary = FileId;
    method summarize (line 1762) | fn summarize(&self) -> Self::Summary {
    type Key (line 1769) | type Key = FileId;
    method key (line 1771) | fn key(&self) -> Self::Key {
  type ParentRefValue (line 132) | struct ParentRefValue {
    type Summary (line 1777) | type Summary = ParentRefValueKey;
    method summarize (line 1779) | fn summarize(&self) -> Self::Summary {
    type Key (line 1786) | type Key = ParentRefValueKey;
    method key (line 1788) | fn key(&self) -> Self::Key {
  type ParentRefValueKey (line 139) | struct ParentRefValueKey {
    method from_summary (line 1797) | fn from_summary(summary: &ParentRefValueKey) -> ParentRefValueKey {
    method add_assign (line 1817) | fn add_assign(&mut self, other: &Self) {
    type Output (line 1824) | type Output = Self;
    method add (line 1826) | fn add(self, other: &Self) -> Self {
  type ChildRefValue (line 145) | struct ChildRefValue {
    type Summary (line 1839) | type Summary = ChildRefValueSummary;
    method summarize (line 1841) | fn summarize(&self) -> Self::Summary {
    type Key (line 1853) | type Key = ChildRefValueKey;
    method key (line 1855) | fn key(&self) -> Self::Key {
  type ChildRefValueSummary (line 154) | pub struct ChildRefValueSummary {
    method add_assign (line 1882) | fn add_assign(&mut self, other: &Self) {
  type ChildRefValueKey (line 163) | struct ChildRefValueKey {
    method from_summary (line 1900) | fn from_summary(summary: &ChildRefValueSummary) -> ChildRefValueKey {
    method add_assign (line 1927) | fn add_assign(&mut self, other: &Self) {
    type Output (line 1934) | type Output = Self;
    method add (line 1936) | fn add(self, other: &Self) -> Self {
  type ChildRefKey (line 171) | pub struct ChildRefKey {
    method from_summary (line 1943) | fn from_summary(summary: &ChildRefValueSummary) -> Self {
    method add_assign (line 1952) | fn add_assign(&mut self, other: &Self) {
    type Output (line 1959) | type Output = Self;
    method add (line 1961) | fn add(self, other: &Self) -> Self {
  type ReplicaLocation (line 177) | struct ReplicaLocation {
  type TextFile (line 183) | enum TextFile {
    method is_modified (line 1974) | fn is_modified(&self) -> bool {
    method is_buffered (line 1982) | fn is_buffered(&self) -> bool {
  function next (line 1257) | pub fn next(&mut self, can_descend: bool) -> bool {
  function entry (line 1274) | pub fn entry(&self) -> Result<CursorEntry, Error> {
  function path (line 1319) | pub fn path(&self) -> Result<&Path, Error> {
  function base_path (line 1327) | pub fn base_path(&self) -> Result<Option<PathBuf>, Error> {
  function descend_into (line 1332) | fn descend_into(&mut self, parent_visible: bool, dir_id: FileId) -> bool {
  function next_sibling (line 1353) | pub fn next_sibling(&mut self) -> bool {
  function is_modified_file (line 1370) | fn is_modified_file(&self, file_id: FileId) -> bool {
  method default (line 1738) | fn default() -> Self {
  method cmp (line 1803) | fn cmp(&self, other: &Self) -> Ordering {
  method partial_cmp (line 1811) | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
  method cmp (line 1866) | fn cmp(&self, other: &Self) -> Ordering {
  method partial_cmp (line 1876) | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
  method cmp (line 1911) | fn cmp(&self, other: &Self) -> Ordering {
  method partial_cmp (line 1921) | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
  function from_summary (line 1968) | fn from_summary(summary: &ChildRefValueSummary) -> Self {
  function serialize_os_string (line 1990) | fn serialize_os_string<S>(os_string: &OsString, serializer: S) -> Result...
  function deserialize_os_string (line 1997) | fn deserialize_os_string<'de, D>(deserializer: D) -> Result<OsString, D:...
  function test_append_base_entries (line 2012) | fn test_append_base_entries() {
  function test_cursor (line 2080) | fn test_cursor() {
  function test_buffers (line 2282) | fn test_buffers() {
  function test_buffer_deferred_ops_len (line 2350) | fn test_buffer_deferred_ops_len() -> Result<(), Error> {
  function test_replication_random (line 2390) | fn test_replication_random() {
  function gen_name (line 2690) | fn gen_name<T: Rng>(rng: &mut T) -> String {

FILE: memo_core/src/lib.rs
  type ReplicaId (line 21) | pub type ReplicaId = Uuid;
  type Oid (line 22) | pub type Oid = [u8; 20];
  type Error (line 25) | pub enum Error {
    method from (line 84) | fn from(error: io::Error) -> Self {
    method fmt (line 90) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  type ReplicaIdExt (line 41) | trait ReplicaIdExt {
    method to_flatbuf (line 42) | fn to_flatbuf(&self) -> serialization::ReplicaId;
    method from_flatbuf (line 43) | fn from_flatbuf(message: &serialization::ReplicaId) -> Self;
    method to_flatbuf (line 47) | fn to_flatbuf(&self) -> serialization::ReplicaId {
    method from_flatbuf (line 60) | fn from_flatbuf(message: &serialization::ReplicaId) -> Self {
  method from (line 78) | fn from(error: Error) -> Self {
  method eq (line 96) | fn eq(&self, other: &Self) -> bool {
  type Envelope (line 127) | struct Envelope<T: Clone> {
  type Network (line 132) | pub(crate) struct Network<T: Clone> {
  function new (line 138) | pub fn new() -> Self {
  function add_peer (line 145) | pub fn add_peer(&mut self, id: ReplicaId) {
  function is_idle (line 149) | pub fn is_idle(&self) -> bool {
  function all_messages (line 153) | pub fn all_messages(&self) -> &Vec<T> {
  function broadcast (line 157) | pub fn broadcast<R>(&mut self, sender: ReplicaId, messages: Vec<T>, rng:...
  function has_unreceived (line 195) | pub fn has_unreceived(&self, receiver: ReplicaId) -> bool {
  function receive (line 199) | pub fn receive<R>(&mut self, receiver: ReplicaId, rng: &mut R) -> Vec<T>
  function clear_unreceived (line 211) | pub fn clear_unreceived(&mut self, receiver: ReplicaId) {

FILE: memo_core/src/operation_queue.rs
  type Operation (line 6) | pub trait Operation: Clone + Debug + Eq {
    method timestamp (line 7) | fn timestamp(&self) -> time::Lamport;
    method timestamp (line 146) | fn timestamp(&self) -> time::Lamport {
  type OperationQueue (line 11) | pub struct OperationQueue<T: Operation>(Tree<T>);
  type OperationKey (line 14) | pub struct OperationKey(time::Lamport);
    method from_summary (line 93) | fn from_summary(summary: &OperationSummary) -> Self {
    type Output (line 99) | type Output = Self;
    method add (line 101) | fn add(self, other: &Self) -> Self {
    method add_assign (line 108) | fn add_assign(&mut self, other: &Self) {
  type OperationSummary (line 17) | pub struct OperationSummary {
    method add_assign (line 73) | fn add_assign(&mut self, other: &Self) {
    type Output (line 81) | type Output = Self;
    method add (line 83) | fn add(self, other: &Self) -> Self {
  function new (line 23) | pub fn new() -> Self {
  function is_empty (line 28) | pub fn is_empty(&self) -> bool {
  function len (line 32) | pub fn len(&self) -> usize {
  function insert (line 36) | pub fn insert(&mut self, mut ops: Vec<T>) {
  function drain (line 46) | pub fn drain(&mut self) -> Cursor<T> {
  type Summary (line 54) | type Summary = OperationSummary;
  method summarize (line 56) | fn summarize(&self) -> Self::Summary {
  type Key (line 65) | type Key = OperationKey;
  method key (line 67) | fn key(&self) -> Self::Key {
  function test_len (line 120) | fn test_len() {
  type TestOperation (line 143) | struct TestOperation(time::Lamport);

FILE: memo_core/src/serialization/schema_generated.rs
  type ReplicaId (line 11) | pub struct ReplicaId {
    type Inner (line 17) | type Inner = &'a ReplicaId;
    method follow (line 19) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    type Output (line 31) | type Output = ReplicaId;
    method push (line 33) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
    method new (line 54) | pub fn new<'a>(_first_8_bytes: u64, _last_8_bytes: u64) -> Self {
    method first_8_bytes (line 61) | pub fn first_8_bytes<'a>(&'a self) -> u64 {
    method last_8_bytes (line 64) | pub fn last_8_bytes<'a>(&'a self) -> u64 {
  type Inner (line 24) | type Inner = &'a ReplicaId;
  function follow (line 26) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  type Output (line 41) | type Output = ReplicaId;
  function push (line 44) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  type Timestamp (line 72) | pub struct Timestamp {
    type Inner (line 78) | type Inner = &'a Timestamp;
    method follow (line 80) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    type Output (line 92) | type Output = Timestamp;
    method push (line 94) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
    method new (line 115) | pub fn new<'a>(_value: u64, _replica_id: &'a ReplicaId) -> Self {
    method value (line 122) | pub fn value<'a>(&'a self) -> u64 {
    method replica_id (line 125) | pub fn replica_id<'a>(&'a self) -> &'a ReplicaId {
  type Inner (line 85) | type Inner = &'a Timestamp;
  function follow (line 87) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  type Output (line 102) | type Output = Timestamp;
  function push (line 105) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  type GlobalTimestampOffset (line 130) | pub enum GlobalTimestampOffset {}
  type GlobalTimestamp (line 133) | pub struct GlobalTimestamp<'a> {
  type Inner (line 138) | type Inner = GlobalTimestamp<'a>;
  function follow (line 140) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 149) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 155) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_TIMESTAMPS (line 163) | pub const VT_TIMESTAMPS: flatbuffers::VOffsetT = 4;
  function timestamps (line 166) | pub fn timestamps(&self) -> Option<&'a [Timestamp]> {
  type GlobalTimestampArgs (line 171) | pub struct GlobalTimestampArgs<'a> {
  method default (line 176) | fn default() -> Self {
  type GlobalTimestampBuilder (line 182) | pub struct GlobalTimestampBuilder<'a: 'b, 'b> {
  function add_timestamps (line 188) | pub fn add_timestamps(&mut self, timestamps: flatbuffers::WIPOffset<flat...
  function new (line 192) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> GlobalTi...
  function finish (line 200) | pub fn finish(self) -> flatbuffers::WIPOffset<GlobalTimestamp<'a>> {
  type AnchorVariant (line 219) | pub enum AnchorVariant {
    type Inner (line 230) | type Inner = Self;
    method follow (line 232) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 239) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 245) | fn from_little_endian(self) -> Self {
    type Output (line 253) | type Output = AnchorVariant;
    method push (line 255) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  constant ENUM_MIN_ANCHOR_VARIANT (line 226) | const ENUM_MIN_ANCHOR_VARIANT: i8 = 0;
  constant ENUM_MAX_ANCHOR_VARIANT (line 227) | const ENUM_MAX_ANCHOR_VARIANT: i8 = 2;
  constant ENUM_VALUES_ANCHOR_VARIANT (line 261) | const ENUM_VALUES_ANCHOR_VARIANT:[AnchorVariant; 3] = [
  constant ENUM_NAMES_ANCHOR_VARIANT (line 268) | const ENUM_NAMES_ANCHOR_VARIANT:[&'static str; 3] = [
  function enum_name_anchor_variant (line 274) | pub fn enum_name_anchor_variant(e: AnchorVariant) -> &'static str {
  type AnchorBias (line 282) | pub enum AnchorBias {
    type Inner (line 292) | type Inner = Self;
    method follow (line 294) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 301) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 307) | fn from_little_endian(self) -> Self {
    type Output (line 315) | type Output = AnchorBias;
    method push (line 317) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  constant ENUM_MIN_ANCHOR_BIAS (line 288) | const ENUM_MIN_ANCHOR_BIAS: i8 = 0;
  constant ENUM_MAX_ANCHOR_BIAS (line 289) | const ENUM_MAX_ANCHOR_BIAS: i8 = 1;
  constant ENUM_VALUES_ANCHOR_BIAS (line 323) | const ENUM_VALUES_ANCHOR_BIAS:[AnchorBias; 2] = [
  constant ENUM_NAMES_ANCHOR_BIAS (line 329) | const ENUM_NAMES_ANCHOR_BIAS:[&'static str; 2] = [
  function enum_name_anchor_bias (line 334) | pub fn enum_name_anchor_bias(e: AnchorBias) -> &'static str {
  type OperationVariant (line 342) | pub enum OperationVariant {
    type Inner (line 353) | type Inner = Self;
    method follow (line 355) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 362) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 368) | fn from_little_endian(self) -> Self {
    type Output (line 376) | type Output = OperationVariant;
    method push (line 378) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
    type Inner (line 2019) | type Inner = Self;
    method follow (line 2021) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 2028) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 2034) | fn from_little_endian(self) -> Self {
    type Output (line 2042) | type Output = OperationVariant;
    method push (line 2044) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  constant ENUM_MIN_OPERATION_VARIANT (line 349) | const ENUM_MIN_OPERATION_VARIANT: u8 = 0;
  constant ENUM_MAX_OPERATION_VARIANT (line 350) | const ENUM_MAX_OPERATION_VARIANT: u8 = 2;
  constant ENUM_VALUES_OPERATION_VARIANT (line 384) | const ENUM_VALUES_OPERATION_VARIANT:[OperationVariant; 3] = [
  constant ENUM_NAMES_OPERATION_VARIANT (line 391) | const ENUM_NAMES_OPERATION_VARIANT:[&'static str; 3] = [
  function enum_name_operation_variant (line 397) | pub fn enum_name_operation_variant(e: OperationVariant) -> &'static str {
  type OperationVariantUnionTableOffset (line 402) | pub struct OperationVariantUnionTableOffset {}
  type AnchorOffset (line 403) | pub enum AnchorOffset {}
  type Anchor (line 406) | pub struct Anchor<'a> {
  type Inner (line 411) | type Inner = Anchor<'a>;
  function follow (line 413) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 422) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 428) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_VARIANT (line 439) | pub const VT_VARIANT: flatbuffers::VOffsetT = 4;
  constant VT_INSERTION_ID (line 440) | pub const VT_INSERTION_ID: flatbuffers::VOffsetT = 6;
  constant VT_OFFSET (line 441) | pub const VT_OFFSET: flatbuffers::VOffsetT = 8;
  constant VT_BIAS (line 442) | pub const VT_BIAS: flatbuffers::VOffsetT = 10;
  function variant (line 445) | pub fn variant(&self) -> AnchorVariant {
  function insertion_id (line 449) | pub fn insertion_id(&self) -> Option<&'a super::Timestamp> {
  function offset (line 453) | pub fn offset(&self) -> u64 {
  function bias (line 457) | pub fn bias(&self) -> AnchorBias {
  type AnchorArgs (line 462) | pub struct AnchorArgs<'a> {
  method default (line 470) | fn default() -> Self {
  type AnchorBuilder (line 479) | pub struct AnchorBuilder<'a: 'b, 'b> {
  function add_variant (line 485) | pub fn add_variant(&mut self, variant: AnchorVariant) {
  function add_insertion_id (line 489) | pub fn add_insertion_id(&mut self, insertion_id: &'b  super::Timestamp) {
  function add_offset (line 493) | pub fn add_offset(&mut self, offset: u64) {
  function add_bias (line 497) | pub fn add_bias(&mut self, bias: AnchorBias) {
  function new (line 501) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> AnchorBu...
  function finish (line 509) | pub fn finish(self) -> flatbuffers::WIPOffset<Anchor<'a>> {
  type SelectionOffset (line 515) | pub enum SelectionOffset {}
  type Selection (line 518) | pub struct Selection<'a> {
  type Inner (line 523) | type Inner = Selection<'a>;
  function follow (line 525) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 534) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 540) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_START (line 550) | pub const VT_START: flatbuffers::VOffsetT = 4;
  constant VT_END (line 551) | pub const VT_END: flatbuffers::VOffsetT = 6;
  constant VT_REVERSED (line 552) | pub const VT_REVERSED: flatbuffers::VOffsetT = 8;
  function start (line 555) | pub fn start(&self) -> Option<Anchor<'a>> {
  function end (line 559) | pub fn end(&self) -> Option<Anchor<'a>> {
  function reversed (line 563) | pub fn reversed(&self) -> bool {
  type SelectionArgs (line 568) | pub struct SelectionArgs<'a> {
  method default (line 575) | fn default() -> Self {
  type SelectionBuilder (line 583) | pub struct SelectionBuilder<'a: 'b, 'b> {
  function add_start (line 589) | pub fn add_start(&mut self, start: flatbuffers::WIPOffset<Anchor<'b >>) {
  function add_end (line 593) | pub fn add_end(&mut self, end: flatbuffers::WIPOffset<Anchor<'b >>) {
  function add_reversed (line 597) | pub fn add_reversed(&mut self, reversed: bool) {
  function new (line 601) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> Selectio...
  function finish (line 609) | pub fn finish(self) -> flatbuffers::WIPOffset<Selection<'a>> {
  type EditOffset (line 615) | pub enum EditOffset {}
  type Edit (line 618) | pub struct Edit<'a> {
  type Inner (line 623) | type Inner = Edit<'a>;
  function follow (line 625) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 634) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 640) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_START_ID (line 655) | pub const VT_START_ID: flatbuffers::VOffsetT = 4;
  constant VT_START_OFFSET (line 656) | pub const VT_START_OFFSET: flatbuffers::VOffsetT = 6;
  constant VT_END_ID (line 657) | pub const VT_END_ID: flatbuffers::VOffsetT = 8;
  constant VT_END_OFFSET (line 658) | pub const VT_END_OFFSET: flatbuffers::VOffsetT = 10;
  constant VT_VERSION_IN_RANGE (line 659) | pub const VT_VERSION_IN_RANGE: flatbuffers::VOffsetT = 12;
  constant VT_NEW_TEXT (line 660) | pub const VT_NEW_TEXT: flatbuffers::VOffsetT = 14;
  constant VT_LOCAL_TIMESTAMP (line 661) | pub const VT_LOCAL_TIMESTAMP: flatbuffers::VOffsetT = 16;
  constant VT_LAMPORT_TIMESTAMP (line 662) | pub const VT_LAMPORT_TIMESTAMP: flatbuffers::VOffsetT = 18;
  function start_id (line 665) | pub fn start_id(&self) -> Option<&'a super::Timestamp> {
  function start_offset (line 669) | pub fn start_offset(&self) -> u64 {
  function end_id (line 673) | pub fn end_id(&self) -> Option<&'a super::Timestamp> {
  function end_offset (line 677) | pub fn end_offset(&self) -> u64 {
  function version_in_range (line 681) | pub fn version_in_range(&self) -> Option<super::GlobalTimestamp<'a>> {
  function new_text (line 685) | pub fn new_text(&self) -> Option<&'a str> {
  function local_timestamp (line 689) | pub fn local_timestamp(&self) -> Option<&'a super::Timestamp> {
  function lamport_timestamp (line 693) | pub fn lamport_timestamp(&self) -> Option<&'a super::Timestamp> {
  type EditArgs (line 698) | pub struct EditArgs<'a> {
  method default (line 710) | fn default() -> Self {
  type EditBuilder (line 723) | pub struct EditBuilder<'a: 'b, 'b> {
  function add_start_id (line 729) | pub fn add_start_id(&mut self, start_id: &'b  super::Timestamp) {
  function add_start_offset (line 733) | pub fn add_start_offset(&mut self, start_offset: u64) {
  function add_end_id (line 737) | pub fn add_end_id(&mut self, end_id: &'b  super::Timestamp) {
  function add_end_offset (line 741) | pub fn add_end_offset(&mut self, end_offset: u64) {
  function add_version_in_range (line 745) | pub fn add_version_in_range(&mut self, version_in_range: flatbuffers::WI...
  function add_new_text (line 749) | pub fn add_new_text(&mut self, new_text: flatbuffers::WIPOffset<&'b  str...
  function add_local_timestamp (line 753) | pub fn add_local_timestamp(&mut self, local_timestamp: &'b  super::Times...
  function add_lamport_timestamp (line 757) | pub fn add_lamport_timestamp(&mut self, lamport_timestamp: &'b  super::T...
  function new (line 761) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> EditBuil...
  function finish (line 769) | pub fn finish(self) -> flatbuffers::WIPOffset<Edit<'a>> {
  type UpdateSelectionsOffset (line 775) | pub enum UpdateSelectionsOffset {}
  type UpdateSelections (line 778) | pub struct UpdateSelections<'a> {
  type Inner (line 783) | type Inner = UpdateSelections<'a>;
  function follow (line 785) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 794) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 800) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_SET_ID (line 810) | pub const VT_SET_ID: flatbuffers::VOffsetT = 4;
  constant VT_SELECTIONS (line 811) | pub const VT_SELECTIONS: flatbuffers::VOffsetT = 6;
  constant VT_LAMPORT_TIMESTAMP (line 812) | pub const VT_LAMPORT_TIMESTAMP: flatbuffers::VOffsetT = 8;
  function set_id (line 815) | pub fn set_id(&self) -> Option<&'a super::Timestamp> {
  function selections (line 819) | pub fn selections(&self) -> Option<flatbuffers::Vector<flatbuffers::Forw...
  function lamport_timestamp (line 823) | pub fn lamport_timestamp(&self) -> Option<&'a super::Timestamp> {
  type UpdateSelectionsArgs (line 828) | pub struct UpdateSelectionsArgs<'a> {
  method default (line 835) | fn default() -> Self {
  type UpdateSelectionsBuilder (line 843) | pub struct UpdateSelectionsBuilder<'a: 'b, 'b> {
  function add_set_id (line 849) | pub fn add_set_id(&mut self, set_id: &'b  super::Timestamp) {
  function add_selections (line 853) | pub fn add_selections(&mut self, selections: flatbuffers::WIPOffset<flat...
  function add_lamport_timestamp (line 857) | pub fn add_lamport_timestamp(&mut self, lamport_timestamp: &'b  super::T...
  function new (line 861) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> UpdateSe...
  function finish (line 869) | pub fn finish(self) -> flatbuffers::WIPOffset<UpdateSelections<'a>> {
  type OperationOffset (line 875) | pub enum OperationOffset {}
  type Operation (line 878) | pub struct Operation<'a> {
    type Inner (line 1135) | type Inner = Self;
    method follow (line 1137) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 1144) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 1150) | fn from_little_endian(self) -> Self {
    type Output (line 1158) | type Output = Operation;
    method push (line 1160) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  type Inner (line 883) | type Inner = Operation<'a>;
  function follow (line 885) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 894) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 900) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_VARIANT_TYPE (line 909) | pub const VT_VARIANT_TYPE: flatbuffers::VOffsetT = 4;
  constant VT_VARIANT (line 910) | pub const VT_VARIANT: flatbuffers::VOffsetT = 6;
  function variant_type (line 913) | pub fn variant_type(&self) -> OperationVariant {
  function variant (line 917) | pub fn variant(&self) -> Option<flatbuffers::Table<'a>> {
  function variant_as_edit (line 922) | pub fn variant_as_edit(&'a self) -> Option<Edit> {
  function variant_as_update_selections (line 932) | pub fn variant_as_update_selections(&'a self) -> Option<UpdateSelections> {
  type OperationArgs (line 942) | pub struct OperationArgs {
  method default (line 948) | fn default() -> Self {
  type OperationBuilder (line 955) | pub struct OperationBuilder<'a: 'b, 'b> {
  function add_variant_type (line 961) | pub fn add_variant_type(&mut self, variant_type: OperationVariant) {
  function add_variant (line 965) | pub fn add_variant(&mut self, variant: flatbuffers::WIPOffset<flatbuffer...
  function new (line 969) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> Operatio...
  function finish (line 977) | pub fn finish(self) -> flatbuffers::WIPOffset<Operation<'a>> {
  type FileId (line 998) | pub enum FileId {
    type Inner (line 1009) | type Inner = Self;
    method follow (line 1011) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 1018) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 1024) | fn from_little_endian(self) -> Self {
    type Output (line 1032) | type Output = FileId;
    method push (line 1034) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  constant ENUM_MIN_FILE_ID (line 1005) | const ENUM_MIN_FILE_ID: u8 = 0;
  constant ENUM_MAX_FILE_ID (line 1006) | const ENUM_MAX_FILE_ID: u8 = 2;
  constant ENUM_VALUES_FILE_ID (line 1040) | const ENUM_VALUES_FILE_ID:[FileId; 3] = [
  constant ENUM_NAMES_FILE_ID (line 1047) | const ENUM_NAMES_FILE_ID:[&'static str; 3] = [
  function enum_name_file_id (line 1053) | pub fn enum_name_file_id(e: FileId) -> &'static str {
  type FileIdUnionTableOffset (line 1058) | pub struct FileIdUnionTableOffset {}
  type FileType (line 1062) | pub enum FileType {
    type Inner (line 1072) | type Inner = Self;
    method follow (line 1074) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 1081) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 1087) | fn from_little_endian(self) -> Self {
    type Output (line 1095) | type Output = FileType;
    method push (line 1097) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  constant ENUM_MIN_FILE_TYPE (line 1068) | const ENUM_MIN_FILE_TYPE: i8 = 0;
  constant ENUM_MAX_FILE_TYPE (line 1069) | const ENUM_MAX_FILE_TYPE: i8 = 1;
  constant ENUM_VALUES_FILE_TYPE (line 1103) | const ENUM_VALUES_FILE_TYPE:[FileType; 2] = [
  constant ENUM_NAMES_FILE_TYPE (line 1109) | const ENUM_NAMES_FILE_TYPE:[&'static str; 2] = [
  function enum_name_file_type (line 1114) | pub fn enum_name_file_type(e: FileType) -> &'static str {
  type Operation (line 1122) | pub enum Operation {
    type Inner (line 1135) | type Inner = Self;
    method follow (line 1137) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 1144) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 1150) | fn from_little_endian(self) -> Self {
    type Output (line 1158) | type Output = Operation;
    method push (line 1160) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  constant ENUM_MIN_OPERATION (line 1131) | const ENUM_MIN_OPERATION: u8 = 0;
  constant ENUM_MAX_OPERATION (line 1132) | const ENUM_MAX_OPERATION: u8 = 4;
  constant ENUM_VALUES_OPERATION (line 1166) | const ENUM_VALUES_OPERATION:[Operation; 5] = [
  constant ENUM_NAMES_OPERATION (line 1175) | const ENUM_NAMES_OPERATION:[&'static str; 5] = [
  function enum_name_operation (line 1183) | pub fn enum_name_operation(e: Operation) -> &'static str {
  type OperationUnionTableOffset (line 1188) | pub struct OperationUnionTableOffset {}
  type BaseFileIdOffset (line 1189) | pub enum BaseFileIdOffset {}
  type BaseFileId (line 1192) | pub struct BaseFileId<'a> {
  type Inner (line 1197) | type Inner = BaseFileId<'a>;
  function follow (line 1199) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 1208) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 1214) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_INDEX (line 1222) | pub const VT_INDEX: flatbuffers::VOffsetT = 4;
  function index (line 1225) | pub fn index(&self) -> u64 {
  type BaseFileIdArgs (line 1230) | pub struct BaseFileIdArgs {
  method default (line 1235) | fn default() -> Self {
  type BaseFileIdBuilder (line 1241) | pub struct BaseFileIdBuilder<'a: 'b, 'b> {
  function add_index (line 1247) | pub fn add_index(&mut self, index: u64) {
  function new (line 1251) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> BaseFile...
  function finish (line 1259) | pub fn finish(self) -> flatbuffers::WIPOffset<BaseFileId<'a>> {
  type NewFileIdOffset (line 1265) | pub enum NewFileIdOffset {}
  type NewFileId (line 1268) | pub struct NewFileId<'a> {
  type Inner (line 1273) | type Inner = NewFileId<'a>;
  function follow (line 1275) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 1284) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 1290) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_ID (line 1298) | pub const VT_ID: flatbuffers::VOffsetT = 4;
  function id (line 1301) | pub fn id(&self) -> Option<&'a super::Timestamp> {
  type NewFileIdArgs (line 1306) | pub struct NewFileIdArgs<'a> {
  method default (line 1311) | fn default() -> Self {
  type NewFileIdBuilder (line 1317) | pub struct NewFileIdBuilder<'a: 'b, 'b> {
  function add_id (line 1323) | pub fn add_id(&mut self, id: &'b  super::Timestamp) {
  function new (line 1327) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> NewFileI...
  function finish (line 1335) | pub fn finish(self) -> flatbuffers::WIPOffset<NewFileId<'a>> {
  type InsertMetadataOffset (line 1341) | pub enum InsertMetadataOffset {}
  type InsertMetadata (line 1344) | pub struct InsertMetadata<'a> {
  type Inner (line 1349) | type Inner = InsertMetadata<'a>;
  function follow (line 1351) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 1360) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 1366) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_FILE_ID_TYPE (line 1381) | pub const VT_FILE_ID_TYPE: flatbuffers::VOffsetT = 4;
  constant VT_FILE_ID (line 1382) | pub const VT_FILE_ID: flatbuffers::VOffsetT = 6;
  constant VT_FILE_TYPE (line 1383) | pub const VT_FILE_TYPE: flatbuffers::VOffsetT = 8;
  constant VT_PARENT_ID_TYPE (line 1384) | pub const VT_PARENT_ID_TYPE: flatbuffers::VOffsetT = 10;
  constant VT_PARENT_ID (line 1385) | pub const VT_PARENT_ID: flatbuffers::VOffsetT = 12;
  constant VT_NAME_IN_PARENT (line 1386) | pub const VT_NAME_IN_PARENT: flatbuffers::VOffsetT = 14;
  constant VT_LOCAL_TIMESTAMP (line 1387) | pub const VT_LOCAL_TIMESTAMP: flatbuffers::VOffsetT = 16;
  constant VT_LAMPORT_TIMESTAMP (line 1388) | pub const VT_LAMPORT_TIMESTAMP: flatbuffers::VOffsetT = 18;
  function file_id_type (line 1391) | pub fn file_id_type(&self) -> FileId {
  function file_id (line 1395) | pub fn file_id(&self) -> Option<flatbuffers::Table<'a>> {
  function file_type (line 1399) | pub fn file_type(&self) -> FileType {
  function parent_id_type (line 1403) | pub fn parent_id_type(&self) -> FileId {
  function parent_id (line 1407) | pub fn parent_id(&self) -> Option<flatbuffers::Table<'a>> {
  function name_in_parent (line 1411) | pub fn name_in_parent(&self) -> Option<&'a str> {
  function local_timestamp (line 1415) | pub fn local_timestamp(&self) -> Option<&'a super::Timestamp> {
  function lamport_timestamp (line 1419) | pub fn lamport_timestamp(&self) -> Option<&'a super::Timestamp> {
  function file_id_as_base_file_id (line 1424) | pub fn file_id_as_base_file_id(&'a self) -> Option<BaseFileId> {
  function file_id_as_new_file_id (line 1434) | pub fn file_id_as_new_file_id(&'a self) -> Option<NewFileId> {
  function parent_id_as_base_file_id (line 1444) | pub fn parent_id_as_base_file_id(&'a self) -> Option<BaseFileId> {
  function parent_id_as_new_file_id (line 1454) | pub fn parent_id_as_new_file_id(&'a self) -> Option<NewFileId> {
  type InsertMetadataArgs (line 1464) | pub struct InsertMetadataArgs<'a> {
  method default (line 1476) | fn default() -> Self {
  type InsertMetadataBuilder (line 1489) | pub struct InsertMetadataBuilder<'a: 'b, 'b> {
  function add_file_id_type (line 1495) | pub fn add_file_id_type(&mut self, file_id_type: FileId) {
  function add_file_id (line 1499) | pub fn add_file_id(&mut self, file_id: flatbuffers::WIPOffset<flatbuffer...
  function add_file_type (line 1503) | pub fn add_file_type(&mut self, file_type: FileType) {
  function add_parent_id_type (line 1507) | pub fn add_parent_id_type(&mut self, parent_id_type: FileId) {
  function add_parent_id (line 1511) | pub fn add_parent_id(&mut self, parent_id: flatbuffers::WIPOffset<flatbu...
  function add_name_in_parent (line 1515) | pub fn add_name_in_parent(&mut self, name_in_parent: flatbuffers::WIPOff...
  function add_local_timestamp (line 1519) | pub fn add_local_timestamp(&mut self, local_timestamp: &'b  super::Times...
  function add_lamport_timestamp (line 1523) | pub fn add_lamport_timestamp(&mut self, lamport_timestamp: &'b  super::T...
  function new (line 1527) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> InsertMe...
  function finish (line 1535) | pub fn finish(self) -> flatbuffers::WIPOffset<InsertMetadata<'a>> {
  type UpdateParentOffset (line 1541) | pub enum UpdateParentOffset {}
  type UpdateParent (line 1544) | pub struct UpdateParent<'a> {
  type Inner (line 1549) | type Inner = UpdateParent<'a>;
  function follow (line 1551) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 1560) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 1566) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_CHILD_ID_TYPE (line 1580) | pub const VT_CHILD_ID_TYPE: flatbuffers::VOffsetT = 4;
  constant VT_CHILD_ID (line 1581) | pub const VT_CHILD_ID: flatbuffers::VOffsetT = 6;
  constant VT_NEW_PARENT_ID_TYPE (line 1582) | pub const VT_NEW_PARENT_ID_TYPE: flatbuffers::VOffsetT = 8;
  constant VT_NEW_PARENT_ID (line 1583) | pub const VT_NEW_PARENT_ID: flatbuffers::VOffsetT = 10;
  constant VT_NEW_NAME_IN_PARENT (line 1584) | pub const VT_NEW_NAME_IN_PARENT: flatbuffers::VOffsetT = 12;
  constant VT_LOCAL_TIMESTAMP (line 1585) | pub const VT_LOCAL_TIMESTAMP: flatbuffers::VOffsetT = 14;
  constant VT_LAMPORT_TIMESTAMP (line 1586) | pub const VT_LAMPORT_TIMESTAMP: flatbuffers::VOffsetT = 16;
  function child_id_type (line 1589) | pub fn child_id_type(&self) -> FileId {
  function child_id (line 1593) | pub fn child_id(&self) -> Option<flatbuffers::Table<'a>> {
  function new_parent_id_type (line 1597) | pub fn new_parent_id_type(&self) -> FileId {
  function new_parent_id (line 1601) | pub fn new_parent_id(&self) -> Option<flatbuffers::Table<'a>> {
  function new_name_in_parent (line 1605) | pub fn new_name_in_parent(&self) -> Option<&'a str> {
  function local_timestamp (line 1609) | pub fn local_timestamp(&self) -> Option<&'a super::Timestamp> {
  function lamport_timestamp (line 1613) | pub fn lamport_timestamp(&self) -> Option<&'a super::Timestamp> {
  function child_id_as_base_file_id (line 1618) | pub fn child_id_as_base_file_id(&'a self) -> Option<BaseFileId> {
  function child_id_as_new_file_id (line 1628) | pub fn child_id_as_new_file_id(&'a self) -> Option<NewFileId> {
  function new_parent_id_as_base_file_id (line 1638) | pub fn new_parent_id_as_base_file_id(&'a self) -> Option<BaseFileId> {
  function new_parent_id_as_new_file_id (line 1648) | pub fn new_parent_id_as_new_file_id(&'a self) -> Option<NewFileId> {
  type UpdateParentArgs (line 1658) | pub struct UpdateParentArgs<'a> {
  method default (line 1669) | fn default() -> Self {
  type UpdateParentBuilder (line 1681) | pub struct UpdateParentBuilder<'a: 'b, 'b> {
  function add_child_id_type (line 1687) | pub fn add_child_id_type(&mut self, child_id_type: FileId) {
  function add_child_id (line 1691) | pub fn add_child_id(&mut self, child_id: flatbuffers::WIPOffset<flatbuff...
  function add_new_parent_id_type (line 1695) | pub fn add_new_parent_id_type(&mut self, new_parent_id_type: FileId) {
  function add_new_parent_id (line 1699) | pub fn add_new_parent_id(&mut self, new_parent_id: flatbuffers::WIPOffse...
  function add_new_name_in_parent (line 1703) | pub fn add_new_name_in_parent(&mut self, new_name_in_parent: flatbuffers...
  function add_local_timestamp (line 1707) | pub fn add_local_timestamp(&mut self, local_timestamp: &'b  super::Times...
  function add_lamport_timestamp (line 1711) | pub fn add_lamport_timestamp(&mut self, lamport_timestamp: &'b  super::T...
  function new (line 1715) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> UpdatePa...
  function finish (line 1723) | pub fn finish(self) -> flatbuffers::WIPOffset<UpdateParent<'a>> {
  type BufferOperationOffset (line 1729) | pub enum BufferOperationOffset {}
  type BufferOperation (line 1732) | pub struct BufferOperation<'a> {
  type Inner (line 1737) | type Inner = BufferOperation<'a>;
  function follow (line 1739) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 1748) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 1754) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_FILE_ID_TYPE (line 1766) | pub const VT_FILE_ID_TYPE: flatbuffers::VOffsetT = 4;
  constant VT_FILE_ID (line 1767) | pub const VT_FILE_ID: flatbuffers::VOffsetT = 6;
  constant VT_OPERATIONS (line 1768) | pub const VT_OPERATIONS: flatbuffers::VOffsetT = 8;
  constant VT_LOCAL_TIMESTAMP (line 1769) | pub const VT_LOCAL_TIMESTAMP: flatbuffers::VOffsetT = 10;
  constant VT_LAMPORT_TIMESTAMP (line 1770) | pub const VT_LAMPORT_TIMESTAMP: flatbuffers::VOffsetT = 12;
  function file_id_type (line 1773) | pub fn file_id_type(&self) -> FileId {
  function file_id (line 1777) | pub fn file_id(&self) -> Option<flatbuffers::Table<'a>> {
  function operations (line 1781) | pub fn operations(&self) -> Option<flatbuffers::Vector<flatbuffers::Forw...
  function local_timestamp (line 1785) | pub fn local_timestamp(&self) -> Option<&'a super::Timestamp> {
  function lamport_timestamp (line 1789) | pub fn lamport_timestamp(&self) -> Option<&'a super::Timestamp> {
  function file_id_as_base_file_id (line 1794) | pub fn file_id_as_base_file_id(&'a self) -> Option<BaseFileId> {
  function file_id_as_new_file_id (line 1804) | pub fn file_id_as_new_file_id(&'a self) -> Option<NewFileId> {
  type BufferOperationArgs (line 1814) | pub struct BufferOperationArgs<'a> {
  method default (line 1823) | fn default() -> Self {
  type BufferOperationBuilder (line 1833) | pub struct BufferOperationBuilder<'a: 'b, 'b> {
  function add_file_id_type (line 1839) | pub fn add_file_id_type(&mut self, file_id_type: FileId) {
  function add_file_id (line 1843) | pub fn add_file_id(&mut self, file_id: flatbuffers::WIPOffset<flatbuffer...
  function add_operations (line 1847) | pub fn add_operations(&mut self, operations: flatbuffers::WIPOffset<flat...
  function add_local_timestamp (line 1851) | pub fn add_local_timestamp(&mut self, local_timestamp: &'b  super::Times...
  function add_lamport_timestamp (line 1855) | pub fn add_lamport_timestamp(&mut self, lamport_timestamp: &'b  super::T...
  function new (line 1859) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> BufferOp...
  function finish (line 1867) | pub fn finish(self) -> flatbuffers::WIPOffset<BufferOperation<'a>> {
  type UpdateActiveLocationOffset (line 1873) | pub enum UpdateActiveLocationOffset {}
  type UpdateActiveLocation (line 1876) | pub struct UpdateActiveLocation<'a> {
  type Inner (line 1881) | type Inner = UpdateActiveLocation<'a>;
  function follow (line 1883) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 1892) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 1898) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_FILE_ID_TYPE (line 1908) | pub const VT_FILE_ID_TYPE: flatbuffers::VOffsetT = 4;
  constant VT_FILE_ID (line 1909) | pub const VT_FILE_ID: flatbuffers::VOffsetT = 6;
  constant VT_LAMPORT_TIMESTAMP (line 1910) | pub const VT_LAMPORT_TIMESTAMP: flatbuffers::VOffsetT = 8;
  function file_id_type (line 1913) | pub fn file_id_type(&self) -> FileId {
  function file_id (line 1917) | pub fn file_id(&self) -> Option<flatbuffers::Table<'a>> {
  function lamport_timestamp (line 1921) | pub fn lamport_timestamp(&self) -> Option<&'a super::Timestamp> {
  function file_id_as_base_file_id (line 1926) | pub fn file_id_as_base_file_id(&'a self) -> Option<BaseFileId> {
  function file_id_as_new_file_id (line 1936) | pub fn file_id_as_new_file_id(&'a self) -> Option<NewFileId> {
  type UpdateActiveLocationArgs (line 1946) | pub struct UpdateActiveLocationArgs<'a> {
  method default (line 1953) | fn default() -> Self {
  type UpdateActiveLocationBuilder (line 1961) | pub struct UpdateActiveLocationBuilder<'a: 'b, 'b> {
  function add_file_id_type (line 1967) | pub fn add_file_id_type(&mut self, file_id_type: FileId) {
  function add_file_id (line 1971) | pub fn add_file_id(&mut self, file_id: flatbuffers::WIPOffset<flatbuffer...
  function add_lamport_timestamp (line 1975) | pub fn add_lamport_timestamp(&mut self, lamport_timestamp: &'b  super::T...
  function new (line 1979) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> UpdateAc...
  function finish (line 1987) | pub fn finish(self) -> flatbuffers::WIPOffset<UpdateActiveLocation<'a>> {
  type OperationVariant (line 2008) | pub enum OperationVariant {
    type Inner (line 353) | type Inner = Self;
    method follow (line 355) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 362) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 368) | fn from_little_endian(self) -> Self {
    type Output (line 376) | type Output = OperationVariant;
    method push (line 378) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
    type Inner (line 2019) | type Inner = Self;
    method follow (line 2021) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 2028) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 2034) | fn from_little_endian(self) -> Self {
    type Output (line 2042) | type Output = OperationVariant;
    method push (line 2044) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  constant ENUM_MIN_OPERATION_VARIANT (line 2015) | const ENUM_MIN_OPERATION_VARIANT: u8 = 0;
  constant ENUM_MAX_OPERATION_VARIANT (line 2016) | const ENUM_MAX_OPERATION_VARIANT: u8 = 2;
  constant ENUM_VALUES_OPERATION_VARIANT (line 2050) | const ENUM_VALUES_OPERATION_VARIANT:[OperationVariant; 3] = [
  constant ENUM_NAMES_OPERATION_VARIANT (line 2057) | const ENUM_NAMES_OPERATION_VARIANT:[&'static str; 3] = [
  function enum_name_operation_variant (line 2063) | pub fn enum_name_operation_variant(e: OperationVariant) -> &'static str {
  type OperationVariantUnionTableOffset (line 2068) | pub struct OperationVariantUnionTableOffset {}
  type StartEpochOffset (line 2069) | pub enum StartEpochOffset {}
  type StartEpoch (line 2072) | pub struct StartEpoch<'a> {
  type Inner (line 2077) | type Inner = StartEpoch<'a>;
  function follow (line 2079) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 2088) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 2094) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_EPOCH_ID (line 2103) | pub const VT_EPOCH_ID: flatbuffers::VOffsetT = 4;
  constant VT_HEAD (line 2104) | pub const VT_HEAD: flatbuffers::VOffsetT = 6;
  function epoch_id (line 2107) | pub fn epoch_id(&self) -> Option<&'a super::Timestamp> {
  function head (line 2111) | pub fn head(&self) -> Option<&'a [u8]> {
  type StartEpochArgs (line 2116) | pub struct StartEpochArgs<'a> {
  method default (line 2122) | fn default() -> Self {
  type StartEpochBuilder (line 2129) | pub struct StartEpochBuilder<'a: 'b, 'b> {
  function add_epoch_id (line 2135) | pub fn add_epoch_id(&mut self, epoch_id: &'b  super::Timestamp) {
  function add_head (line 2139) | pub fn add_head(&mut self, head: flatbuffers::WIPOffset<flatbuffers::Vec...
  function new (line 2143) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> StartEpo...
  function finish (line 2151) | pub fn finish(self) -> flatbuffers::WIPOffset<StartEpoch<'a>> {
  type EpochOperationOffset (line 2157) | pub enum EpochOperationOffset {}
  type EpochOperation (line 2160) | pub struct EpochOperation<'a> {
  type Inner (line 2165) | type Inner = EpochOperation<'a>;
  function follow (line 2167) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 2176) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 2182) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_EPOCH_ID (line 2192) | pub const VT_EPOCH_ID: flatbuffers::VOffsetT = 4;
  constant VT_OPERATION_TYPE (line 2193) | pub const VT_OPERATION_TYPE: flatbuffers::VOffsetT = 6;
  constant VT_OPERATION (line 2194) | pub const VT_OPERATION: flatbuffers::VOffsetT = 8;
  function epoch_id (line 2197) | pub fn epoch_id(&self) -> Option<&'a super::Timestamp> {
  function operation_type (line 2201) | pub fn operation_type(&self) -> super::epoch::Operation {
  function operation (line 2205) | pub fn operation(&self) -> Option<flatbuffers::Table<'a>> {
  function operation_as_insert_metadata (line 2210) | pub fn operation_as_insert_metadata(&'a self) -> Option<super::epoch::In...
  function operation_as_update_parent (line 2220) | pub fn operation_as_update_parent(&'a self) -> Option<super::epoch::Upda...
  function operation_as_buffer_operation (line 2230) | pub fn operation_as_buffer_operation(&'a self) -> Option<super::epoch::B...
  function operation_as_update_active_location (line 2240) | pub fn operation_as_update_active_location(&'a self) -> Option<super::ep...
  type EpochOperationArgs (line 2250) | pub struct EpochOperationArgs<'a> {
  method default (line 2257) | fn default() -> Self {
  type EpochOperationBuilder (line 2265) | pub struct EpochOperationBuilder<'a: 'b, 'b> {
  function add_epoch_id (line 2271) | pub fn add_epoch_id(&mut self, epoch_id: &'b  super::Timestamp) {
  function add_operation_type (line 2275) | pub fn add_operation_type(&mut self, operation_type: super::epoch::Opera...
  function add_operation (line 2279) | pub fn add_operation(&mut self, operation: flatbuffers::WIPOffset<flatbu...
  function new (line 2283) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> EpochOpe...
  function finish (line 2291) | pub fn finish(self) -> flatbuffers::WIPOffset<EpochOperation<'a>> {
  type OperationOffset (line 2297) | pub enum OperationOffset {}
  type Operation (line 2300) | pub struct Operation<'a> {
    type Inner (line 1135) | type Inner = Self;
    method follow (line 1137) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
    method to_little_endian (line 1144) | fn to_little_endian(self) -> Self {
    method from_little_endian (line 1150) | fn from_little_endian(self) -> Self {
    type Output (line 1158) | type Output = Operation;
    method push (line 1160) | fn push(&self, dst: &mut [u8], _rest: &[u8]) {
  type Inner (line 2305) | type Inner = Operation<'a>;
  function follow (line 2307) | fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
  function init_from_table (line 2316) | pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
  function create (line 2322) | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
  constant VT_VARIANT_TYPE (line 2331) | pub const VT_VARIANT_TYPE: flatbuffers::VOffsetT = 4;
  constant VT_VARIANT (line 2332) | pub const VT_VARIANT: flatbuffers::VOffsetT = 6;
  function variant_type (line 2335) | pub fn variant_type(&self) -> OperationVariant {
  function variant (line 2339) | pub fn variant(&self) -> Option<flatbuffers::Table<'a>> {
  function variant_as_start_epoch (line 2344) | pub fn variant_as_start_epoch(&'a self) -> Option<StartEpoch> {
  function variant_as_epoch_operation (line 2354) | pub fn variant_as_epoch_operation(&'a self) -> Option<EpochOperation> {
  type OperationArgs (line 2364) | pub struct OperationArgs {
  method default (line 2370) | fn default() -> Self {
  type OperationBuilder (line 2377) | pub struct OperationBuilder<'a: 'b, 'b> {
  function add_variant_type (line 2383) | pub fn add_variant_type(&mut self, variant_type: OperationVariant) {
  function add_variant (line 2387) | pub fn add_variant(&mut self, variant: flatbuffers::WIPOffset<flatbuffer...
  function new (line 2391) | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> Operatio...
  function finish (line 2399) | pub fn finish(self) -> flatbuffers::WIPOffset<Operation<'a>> {
  function get_root_as_operation (line 2406) | pub fn get_root_as_operation<'a>(buf: &'a [u8]) -> Operation<'a> {
  function get_size_prefixed_root_as_operation (line 2411) | pub fn get_size_prefixed_root_as_operation<'a>(buf: &'a [u8]) -> Operati...
  function finish_operation_buffer (line 2416) | pub fn finish_operation_buffer<'a, 'b>(
  function finish_size_prefixed_operation_buffer (line 2423) | pub fn finish_size_prefixed_operation_buffer<'a, 'b>(fbb: &'b mut flatbu...

FILE: memo_core/src/time.rs
  type Local (line 15) | pub struct Local {
    method new (line 38) | pub fn new(replica_id: ReplicaId) -> Self {
    method tick (line 45) | pub fn tick(&mut self) -> Self {
    method observe (line 51) | pub fn observe(&mut self, timestamp: Self) {
    method to_flatbuf (line 57) | pub fn to_flatbuf(&self) -> serialization::Timestamp {
    method from_flatbuf (line 61) | pub fn from_flatbuf(message: &serialization::Timestamp) -> Self {
    type Output (line 70) | type Output = Local;
    method add (line 72) | fn add(self, other: &'a Self) -> Self::Output {
    method add_assign (line 78) | fn add_assign(&mut self, other: &Self) {
  type Global (line 21) | pub struct Global(
    method new (line 86) | pub fn new() -> Self {
    method serialize_inner (line 90) | fn serialize_inner<S>(
    method deserialize_inner (line 101) | fn deserialize_inner<'de, D>(deserializer: D) -> Result<Arc<HashMap<Re...
    method get (line 109) | pub fn get(&self, replica_id: ReplicaId) -> u64 {
    method observe (line 113) | pub fn observe(&mut self, timestamp: Local) {
    method observe_all (line 119) | pub fn observe_all(&mut self, other: &Self) {
    method observed (line 128) | pub fn observed(&self, timestamp: Local) -> bool {
    method changed_since (line 132) | pub fn changed_since(&self, other: &Self) -> bool {
    method to_flatbuf (line 138) | pub fn to_flatbuf<'fbb>(
    method from_flatbuf (line 156) | pub fn from_flatbuf<'fbb>(
  type Lamport (line 32) | pub struct Lamport {
    method new (line 189) | pub fn new(replica_id: ReplicaId) -> Self {
    method tick (line 196) | pub fn tick(&mut self) -> Self {
    method observe (line 202) | pub fn observe(&mut self, timestamp: Self) {
    method to_flatbuf (line 206) | pub fn to_flatbuf(&self) -> serialization::Timestamp {
    method from_flatbuf (line 210) | pub fn from_flatbuf(message: &serialization::Timestamp) -> Self {
    method to_bytes (line 217) | pub fn to_bytes(&self) -> [u8; 24] {
  method partial_cmp (line 170) | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {

FILE: memo_core/src/work_tree.rs
  type GitProvider (line 17) | pub trait GitProvider {
    method base_entries (line 18) | fn base_entries(&self, oid: Oid) -> Box<Stream<Item = DirEntry, Error ...
    method base_text (line 19) | fn base_text(&self, oid: Oid, path: &Path) -> Box<Future<Item = String...
    method base_entries (line 1920) | fn base_entries(&self, oid: Oid) -> Box<Stream<Item = DirEntry, Error ...
    method base_text (line 1930) | fn base_text(
  type ChangeObserver (line 22) | pub trait ChangeObserver {
    method changed (line 23) | fn changed(&self, buffer_id: BufferId, changes: Vec<Change>, selection...
    method changed (line 1988) | fn changed(
  type WorkTree (line 26) | pub struct WorkTree {
    method new (line 99) | pub fn new<I>(
    method head (line 137) | pub fn head(&self) -> Option<Oid> {
    method epoch_id (line 141) | pub fn epoch_id(&self) -> epoch::Id {
    method reset (line 145) | pub fn reset(
    method apply_ops (line 157) | pub fn apply_ops<I>(
    method start_epoch (line 236) | fn start_epoch(
    method observed (line 300) | pub fn observed(&self, other: Version) -> bool {
    method version (line 309) | pub fn version(&self) -> Version {
    method with_cursor (line 317) | pub fn with_cursor<F>(&self, mut f: F)
    method create_file (line 326) | pub fn create_file<P>(&self, path: P, file_type: FileType) -> Result<O...
    method rename (line 354) | pub fn rename<P1, P2>(&self, old_path: P1, new_path: P2) -> Result<Ope...
    method set_active_location (line 387) | pub fn set_active_location(
    method replica_locations (line 407) | pub fn replica_locations(&self) -> HashMap<ReplicaId, PathBuf> {
    method remove (line 418) | pub fn remove<P>(&self, path: P) -> Result<OperationEnvelope, Error>
    method exists (line 433) | pub fn exists<P>(&self, path: P) -> bool
    method open_text_file (line 440) | pub fn open_text_file<P>(&self, path: P) -> Box<Future<Item = BufferId...
    method open_text_file_internal (line 454) | fn open_text_file_internal(
    method existing_buffer (line 501) | fn existing_buffer(
    method base_text (line 517) | fn base_text(
    method edit (line 539) | pub fn edit<I, T>(
    method edit_2d (line 567) | pub fn edit_2d<I, T>(
    method add_selection_set (line 595) | pub fn add_selection_set<I>(
    method replace_selection_set (line 621) | pub fn replace_selection_set<I>(
    method remove_selection_set (line 646) | pub fn remove_selection_set(
    method path (line 671) | pub fn path(&self, buffer_id: BufferId) -> Option<PathBuf> {
    method text (line 678) | pub fn text(&self, buffer_id: BufferId) -> Result<buffer::Iter, Error> {
    method selection_ranges (line 683) | pub fn selection_ranges(&self, buffer_id: BufferId) -> Result<BufferSe...
    method selection_ranges_internal (line 692) | fn selection_ranges_internal(
    method changes_since (line 732) | pub fn changes_since(
    method buffer_deferred_ops_len (line 741) | pub fn buffer_deferred_ops_len(&self, buffer_id: BufferId) -> Result<u...
    method cur_epoch (line 746) | fn cur_epoch(&self) -> Ref<Epoch> {
    method cur_epoch_mut (line 750) | fn cur_epoch_mut(&self) -> RefMut<Epoch> {
    method defer_epoch_op (line 754) | fn defer_epoch_op(&self, epoch_id: epoch::Id, operation: epoch::Operat...
    method replica_id (line 762) | fn replica_id(&self) -> ReplicaId {
    method buffer_file_id (line 766) | fn buffer_file_id(&self, buffer_id: BufferId) -> Result<FileId, Error> {
    method gen_local_set_id (line 774) | fn gen_local_set_id(&self) -> LocalSelectionSetId {
    method selection_set_id (line 780) | fn selection_set_id(
    method empty (line 1722) | fn empty() -> Self {
    method entries (line 1734) | fn entries(&self) -> Vec<CursorEntry> {
    method dir_entries (line 1738) | fn dir_entries(&self) -> Vec<DirEntry> {
    method open_buffers (line 1742) | fn open_buffers(&self) -> Vec<BufferId> {
    method text_str (line 1746) | fn text_str(&self, buffer_id: BufferId) -> String {
    method randomly_mutate (line 1750) | fn randomly_mutate<T: Rng>(&self, rng: &mut T, count: usize) -> Vec<Op...
    method open_random_buffers (line 1785) | fn open_random_buffers<T: Rng>(
    method visible_paths (line 1800) | fn visible_paths(&self, file_type: FileType) -> Vec<PathBuf> {
    method select_path (line 1820) | fn select_path<T: Rng>(&self, rng: &mut T, file_type: FileType) -> Opt...
    method update_local_selection_sets (line 1829) | fn update_local_selection_sets(&self) {
    method replica_location (line 1859) | fn replica_location(&self, replica_id: ReplicaId) -> Option<String> {
  type Version (line 40) | pub struct Version {
  type OperationEnvelope (line 45) | pub struct OperationEnvelope {
    method wrap (line 796) | fn wrap(epoch_id: epoch::Id, epoch_head: Option<Oid>, operation: epoch...
    method wrap_many (line 806) | fn wrap_many<T>(epoch_id: epoch::Id, epoch_head: Option<Oid>, operatio...
  type Operation (line 51) | pub enum Operation {
    method epoch_id (line 824) | pub fn epoch_id(&self) -> epoch::Id {
    method is_selection_update (line 831) | pub fn is_selection_update(&self) -> bool {
    method serialize (line 846) | pub fn serialize(&self) -> Vec<u8> {
    method deserialize (line 855) | pub fn deserialize<'a>(buffer: &'a [u8]) -> Result<Option<Self>, Error> {
    method to_flatbuf (line 861) | pub fn to_flatbuf<'fbb>(
    method from_flatbuf (line 913) | pub fn from_flatbuf<'fbb>(
  type BufferId (line 63) | pub struct BufferId(u32);
  type LocalSelectionSetId (line 66) | pub struct LocalSelectionSetId(u32);
  type BufferSelectionRanges (line 69) | pub struct BufferSelectionRanges {
  type MaybeDone (line 74) | enum MaybeDone<F: Future> {
  type BaseTextRequest (line 79) | struct BaseTextRequest {
  type SwitchEpoch (line 84) | struct SwitchEpoch {
    method new (line 953) | fn new(
  type Item (line 982) | type Item = Vec<OperationEnvelope>;
  type Error (line 983) | type Error = Error;
  method poll (line 985) | fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
  function is_done (line 1170) | fn is_done(&self) -> bool {
  function poll (line 1177) | fn poll(&mut self) {
  function take_result (line 1188) | fn take_result(self) -> Option<Result<F::Item, F::Error>> {
  function test_random (line 1204) | fn test_random() {
  function test_reset (line 1322) | fn test_reset() {
  function test_selections_across_resets (line 1421) | fn test_selections_across_resets() {
  function test_active_location_across_resets (line 1550) | fn test_active_location_across_resets() {
  function test_exists (line 1622) | fn test_exists() {
  function test_version (line 1643) | fn test_version() {
  function open_envelopes (line 1701) | fn open_envelopes<I: IntoIterator<Item = OperationEnvelope>>(envelopes: ...
  function serialize_ops (line 1705) | fn serialize_ops<I: IntoIterator<Item = Operation>>(ops: I) -> Vec<Vec<u...
  function deserialize_ops (line 1709) | fn deserialize_ops<I: IntoIterator<Item = Vec<u8>>>(ops: I) -> Vec<Opera...
  type BufferSelections (line 1716) | struct BufferSelections {
  type TestGitProvider (line 1866) | struct TestGitProvider {
    method new (line 1879) | fn new() -> Self {
    method commit (line 1886) | fn commit(&self, tree: &WorkTree) -> Oid {
    method tree (line 1899) | fn tree(&self, oid: Oid) -> Ref<WorkTree> {
    method gen_oid (line 1903) | fn gen_oid(&self) -> Oid {
  type TestChangeObserver (line 1871) | struct TestChangeObserver {
    method new (line 1959) | fn new() -> Self {
    method opened_buffer (line 1968) | fn opened_buffer(&self, buffer_id: BufferId, tree: &WorkTree) {
    method text (line 1978) | fn text(&self, buffer_id: BufferId) -> String {
    method selection_ranges (line 1982) | fn selection_ranges(&self, buffer_id: BufferId) -> BufferSelectionRang...

FILE: memo_js/src/index.ts
  function init (line 34) | async function init() {
  type Version (line 43) | type Version = Tagged<Uint8Array, "Version">;
  type Operation (line 44) | type Operation = Tagged<Uint8Array, "Operation">;
  type EpochId (line 45) | type EpochId = Tagged<Uint8Array, "EpochId">;
  type OperationEnvelope (line 46) | interface OperationEnvelope {
  type FileStatus (line 55) | enum FileStatus {
  type Entry (line 64) | interface Entry {
  class WorkTree (line 74) | class WorkTree {
    method create (line 79) | static async create(
    method constructor (line 98) | private constructor(tree: any, observer: ChangeObserver) {
    method version (line 103) | version(): Version {
    method hasObserved (line 107) | hasObserved(version: Version): boolean {
    method head (line 111) | head(): null | Oid {
    method epochId (line 115) | epochId(): EpochId {
    method reset (line 119) | reset(base: Oid | null): AsyncIterable<OperationEnvelope> {
    method applyOps (line 123) | applyOps(ops: Operation[]): AsyncIterable<OperationEnvelope> {
    method createFile (line 127) | createFile(path: Path, fileType: FileType): OperationEnvelope {
    method rename (line 131) | rename(oldPath: Path, newPath: Path): OperationEnvelope {
    method remove (line 135) | remove(path: Path): OperationEnvelope {
    method exists (line 139) | exists(path: Path): boolean {
    method entries (line 143) | entries(options?: { descendInto?: Path[]; showDeleted?: boolean }): En...
    method openTextFile (line 153) | async openTextFile(path: Path): Promise<Buffer> {
    method setActiveLocation (line 163) | setActiveLocation(buffer: Buffer | null): OperationEnvelope {
    method getReplicaLocations (line 167) | getReplicaLocations(): Map<ReplicaId, Path> {
  class Buffer (line 178) | class Buffer {
    method constructor (line 183) | constructor(id: BufferId, tree: any, observer: ChangeObserver) {
    method edit (line 189) | edit(oldRanges: Range[], newText: string): OperationEnvelope {
    method addSelectionSet (line 193) | addSelectionSet(ranges: Range[]): [SelectionSetId, OperationEnvelope] {
    method replaceSelectionSet (line 198) | replaceSelectionSet(id: SelectionSetId, ranges: Range[]): OperationEnv...
    method removeSelectionSet (line 202) | removeSelectionSet(id: SelectionSetId): OperationEnvelope {
    method getPath (line 206) | getPath(): string | null {
    method getText (line 210) | getText(): string {
    method getSelectionRanges (line 214) | getSelectionRanges(): SelectionRanges {
    method onChange (line 219) | onChange(callback: ChangeObserverCallback): Disposable {
    method getDeferredOperationCount (line 223) | getDeferredOperationCount(): number {

FILE: memo_js/src/lib.rs
  type JsValueExt (line 18) | trait JsValueExt {
    method into_operation (line 19) | fn into_operation(self) -> Result<Option<memo::Operation>, JsValue>;
    method into_ranges_vec (line 20) | fn into_ranges_vec(self) -> Result<Vec<Range<memo::Point>>, JsValue>;
    method into_error_message (line 21) | fn into_error_message(self) -> Result<String, String>;
    method into_operation (line 666) | fn into_operation(self) -> Result<Option<memo::Operation>, JsValue> {
    method into_ranges_vec (line 675) | fn into_ranges_vec(self) -> Result<Vec<Range<memo::Point>>, JsValue> {
    method into_error_message (line 684) | fn into_error_message(self) -> Result<String, String> {
  type IntoJsError (line 24) | trait IntoJsError {
    method into_js_err (line 25) | fn into_js_err(self) -> JsValue;
    method into_js_err (line 660) | fn into_js_err(self) -> JsValue {
  type WorkTree (line 29) | pub struct WorkTree(memo::WorkTree);
    method new (line 123) | pub fn new(
    method version (line 166) | pub fn version(&self) -> Vec<u8> {
    method observed (line 170) | pub fn observed(&self, version_bytes: &[u8]) -> Result<bool, JsValue> {
    method head (line 175) | pub fn head(&self) -> JsValue {
    method epoch_id (line 179) | pub fn epoch_id(&self) -> Vec<u8> {
    method reset (line 183) | pub fn reset(&mut self, base: JsValue) -> Result<StreamToAsyncIterator...
    method apply_ops (line 196) | pub fn apply_ops(&mut self, js_ops: js_sys::Array) -> Result<StreamToA...
    method create_file (line 216) | pub fn create_file(
    method rename (line 228) | pub fn rename(&self, old_path: String, new_path: String) -> Result<Ope...
    method remove (line 235) | pub fn remove(&self, path: String) -> Result<OperationEnvelope, JsValu...
    method exists (line 242) | pub fn exists(&self, path: String) -> bool {
    method set_active_location (line 246) | pub fn set_active_location(&self, buffer_id: JsValue) -> Result<Operat...
    method replica_locations (line 254) | pub fn replica_locations(&self) -> JsValue {
    method open_text_file (line 258) | pub fn open_text_file(&mut self, path: String) -> js_sys::Promise {
    method path (line 267) | pub fn path(&self, buffer_id: JsValue) -> Result<Option<String>, JsVal...
    method text (line 275) | pub fn text(&self, buffer_id: JsValue) -> Result<JsValue, JsValue> {
    method buffer_deferred_ops_len (line 283) | pub fn buffer_deferred_ops_len(&self, buffer_id: JsValue) -> Result<u3...
    method edit (line 291) | pub fn edit(
    method add_selection_set (line 305) | pub fn add_selection_set(
    method replace_selection_set (line 322) | pub fn replace_selection_set(
    method remove_selection_set (line 338) | pub fn remove_selection_set(
    method selection_ranges (line 352) | pub fn selection_ranges(&self, buffer_id: JsValue) -> Result<JsValue, ...
    method entries (line 362) | pub fn entries(&self, descend_into: JsValue, show_deleted: bool) -> Re...
  type AsyncResult (line 32) | struct AsyncResult<T> {
  type AsyncIteratorToStream (line 37) | struct AsyncIteratorToStream<T> {
  type StreamToAsyncIterator (line 44) | pub struct StreamToAsyncIterator(Rc<Cell<Option<Box<Stream<Item = JsValu...
    method new (line 491) | fn new<S>(stream: S) -> Self
    method next (line 513) | pub fn next(&mut self) -> Option<js_sys::Promise> {
  type WorkTreeNewResult (line 47) | pub struct WorkTreeNewResult {
    method tree (line 394) | pub fn tree(&mut self) -> Result<WorkTree, JsValue> {
    method operations (line 400) | pub fn operations(&mut self) -> Result<StreamToAsyncIterator, JsValue> {
  type AddSelectionSetResult (line 53) | pub struct AddSelectionSetResult {
    method set_id (line 409) | pub fn set_id(&mut self) -> JsValue {
    method operation (line 413) | pub fn operation(&mut self) -> Result<OperationEnvelope, JsValue> {
  type OperationEnvelope (line 59) | pub struct OperationEnvelope(memo::OperationEnvelope);
    method new (line 422) | fn new(operation: memo::OperationEnvelope) -> Self {
    method epoch_id (line 427) | pub fn epoch_id(&self) -> Vec<u8> {
    method epoch_replica_id (line 432) | pub fn epoch_replica_id(&self) -> JsValue {
    method epoch_timestamp (line 437) | pub fn epoch_timestamp(&self) -> JsValue {
    method epoch_head (line 442) | pub fn epoch_head(&self) -> JsValue {
    method operation (line 446) | pub fn operation(&self) -> Vec<u8> {
    method is_selection_update (line 451) | pub fn is_selection_update(&self) -> bool {
  type Change (line 62) | struct Change {
  type Entry (line 69) | struct Entry {
  type JsRange (line 82) | struct JsRange {
    method from (line 621) | fn from(range: Range<memo::Point>) -> Self {
  type JsSelections (line 88) | struct JsSelections {
    method from (line 595) | fn from(selections: memo::BufferSelectionRanges) -> Self {
  type HexOid (line 93) | pub struct HexOid(memo::Oid);
    method deserialize (line 639) | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  function next (line 100) | fn next(this: &AsyncIteratorWrapper) -> js_sys::Promise;
  function base_entries (line 105) | fn base_entries(this: &GitProviderWrapper, head: &str) -> AsyncIteratorW...
  function base_text (line 108) | fn base_text(this: &GitProviderWrapper, head: &str, path: &str) -> js_sy...
  function changed (line 113) | fn changed(
  function new (line 457) | fn new(iterator: AsyncIteratorWrapper) -> Self {
  type Item (line 470) | type Item = T;
  type Error (line 471) | type Error = String;
  method poll (line 473) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  method base_entries (line 537) | fn base_entries(
  method base_text (line 548) | fn base_text(
  method changed (line 571) | fn changed(
  method serialize (line 630) | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>

FILE: memo_js/src/support.ts
  type Tagged (line 1) | type Tagged<BaseType, TagName> = BaseType & { __tag: TagName };
  type Oid (line 2) | type Oid = string;
  type Path (line 3) | type Path = string;
  type ReplicaId (line 4) | type ReplicaId = Tagged<string, "ReplicaId">;
  type BufferId (line 5) | type BufferId = Tagged<number, "BufferId">;
  type SelectionSetId (line 6) | type SelectionSetId = Tagged<number, "SelectionSetId">;
  type Point (line 7) | type Point = { row: number; column: number };
  type Range (line 8) | type Range = { start: Point; end: Point };
  type Change (line 9) | type Change = Range & { text: string };
  type BaseEntry (line 11) | interface BaseEntry {
  type FileType (line 17) | enum FileType {
  type GitProvider (line 22) | interface GitProvider {
  type SelectionRanges (line 27) | interface SelectionRanges {
  type MemoSelectionRanges (line 32) | interface MemoSelectionRanges {
  class GitProviderWrapper (line 37) | class GitProviderWrapper {
    method constructor (line 40) | constructor(git: GitProvider) {
    method baseEntries (line 44) | baseEntries(oid: Oid): AsyncIteratorWrapper<BaseEntry> {
    method baseText (line 50) | baseText(oid: Oid, path: Path): Promise<string> {
  class AsyncIteratorWrapper (line 55) | class AsyncIteratorWrapper<T> {
    method constructor (line 58) | constructor(iterator: AsyncIterator<T>) {
    method next (line 62) | next(): Promise<IteratorResult<T>> {
  type ChangeObserverCallback (line 67) | type ChangeObserverCallback = (
  class ChangeObserver (line 74) | class ChangeObserver {
    method constructor (line 77) | constructor() {
    method onChange (line 81) | onChange(bufferId: BufferId, callback: ChangeObserverCallback): Dispos...
    method changed (line 85) | changed(
  function fromMemoSelectionRanges (line 97) | function fromMemoSelectionRanges(
  type Disposable (line 113) | interface Disposable {
  class CompositeDisposable (line 117) | class CompositeDisposable implements Disposable {
    method constructor (line 121) | constructor() {
    method add (line 126) | add(disposable: Disposable) {
    method dispose (line 130) | dispose() {
  type EmitterCallback (line 140) | type EmitterCallback = (params: any) => void;
  class Emitter (line 141) | class Emitter {
    method constructor (line 144) | constructor() {
    method emit (line 148) | emit(eventName: string, params: any) {
    method on (line 157) | on(eventName: string, callback: EmitterCallback): Disposable {

FILE: memo_js/test/tests.ts
  method baseEntries (line 574) | async *baseEntries(): AsyncIterable<BaseEntry> {
  method baseText (line 578) | async baseText(): Promise<string> {
  method baseEntries (line 590) | async *baseEntries(): AsyncIterable<BaseEntry> {
  method baseText (line 594) | async baseText(): Promise<string> {
  type BaseEntry (line 606) | type BaseEntry =
  function collect (line 610) | async function collect<T>(iterable: AsyncIterable<T>): Promise<T[]> {
  function collectOps (line 618) | async function collectOps(
  function point (line 625) | function point(row: number, column: number): Point {
  type ParsedEpochId (line 629) | type ParsedEpochId = { timestamp: number; replicaId: ReplicaId };
  function parseEpochId (line 631) | function parseEpochId(epochId: Uint8Array): ParsedEpochId {
  function mapToObject (line 642) | function mapToObject<T>(map: Map<string | number, T>): { [key: string]: ...
  function last (line 650) | function last<T>(array: ArrayLike<T>): undefined | T {
  class TestGitProvider (line 654) | class TestGitProvider implements GitProvider {
    method constructor (line 658) | constructor() {
    method commit (line 663) | commit(oid: Oid, entries: ReadonlyArray<BaseEntry>) {
    method baseEntries (line 678) | async *baseEntries(oid: Oid): AsyncIterable<BaseEntry> {
    method baseText (line 689) | async baseText(oid: Oid, path: Path): Promise<string> {

FILE: xray_browser/src/client.js
  class XrayClient (line 1) | class XrayClient {
    method constructor (line 2) | constructor(worker) {
    method onMessage (line 6) | onMessage(callback) {
    method sendMessage (line 12) | sendMessage(message) {

FILE: xray_browser/src/worker.js
  class Server (line 13) | class Server {
    method constructor (line 14) | constructor(xray) {
    method startWindow (line 32) | startWindow(windowId) {
    method connectToWebsocket (line 46) | connectToWebsocket(url) {
    method handleMessage (line 67) | handleMessage(message) {

FILE: xray_cli/src/main.rs
  constant USAGE (line 16) | const USAGE: &'static str = "
  type PortNumber (line 30) | type PortNumber = u16;
  type ServerRequest (line 34) | enum ServerRequest {
  type ServerResponse (line 43) | enum ServerResponse {
  type Args (line 49) | struct Args {
  function main (line 57) | fn main() {
  function launch (line 67) | fn launch() -> Result<(), String> {
  function start_headless (line 125) | fn start_headless(server_bin_path: &Path, socket_path: &Path) -> Result<...
  function start_electron (line 144) | fn start_electron(
  function send_message (line 173) | fn send_message(socket: &mut UnixStream, message: ServerRequest) -> Resu...

FILE: xray_core/benches/bench.rs
  function add_selection (line 11) | fn add_selection(c: &mut Criterion) {
  function edit (line 38) | fn edit(c: &mut Criterion) {
  function create_buffer_view (line 57) | fn create_buffer_view(lines: usize) -> BufferView {

FILE: xray_core/src/app.rs
  type WindowId (line 21) | pub type WindowId = usize;
  type WorkspaceId (line 22) | type WorkspaceId = usize;
  type PeerId (line 23) | type PeerId = usize;
  type App (line 25) | pub struct App {
    method new (line 104) | pub fn new<T: 'static + fs::FileProvider>(
    method commands (line 147) | pub fn commands(&mut self) -> Option<UnboundedReceiver<Command>> {
    method headless (line 151) | pub fn headless(&self) -> bool {
    method open_local_workspace (line 155) | pub fn open_local_workspace<T: 'static + fs::LocalTree>(&mut self, roo...
    method add_workspace (line 162) | fn add_workspace(&mut self, workspace: WorkspaceEntry) {
    method open_workspace_window (line 169) | fn open_workspace_window<T: 'static + Workspace>(&mut self, workspace:...
    method start_window (line 194) | pub fn start_window(&mut self, id: &WindowId, height: f64) -> Result<W...
    method dispatch_action (line 200) | pub fn dispatch_action(
    method close_window (line 212) | pub fn close_window(&mut self, window_id: WindowId) -> Result<(), ()> {
    method connect_to_client (line 216) | pub fn connect_to_client<S>(app: Rc<RefCell<App>>, incoming: S) -> ser...
    method connect_to_server (line 223) | pub fn connect_to_server<S>(
  type Command (line 40) | pub enum Command {
  type PeerList (line 44) | pub struct PeerList {
    method new (line 235) | fn new(foreground: ForegroundExecutor) -> Self {
    method state (line 248) | fn state(&self) -> Vec<PeerState> {
    method updates (line 264) | fn updates(&self) -> NotifyCellObserver<()> {
    method take_opened_workspaces (line 268) | fn take_opened_workspaces(&mut self) -> Option<UnboundedReceiver<Remot...
    method connect_to_server (line 272) | fn connect_to_server<S>(
    method open_first_workspace (line 306) | fn open_first_workspace(&self, peer_id: PeerId) {
  type Peer (line 53) | struct Peer {
    method new (line 332) | fn new(foreground: ForegroundExecutor, service: client::Service<AppSer...
    method updates (line 339) | fn updates(&self) -> Result<Box<Stream<Item = (), Error = ()>>, rpc::E...
    method open_first_workspace (line 343) | fn open_first_workspace(
    method open_workspace (line 356) | fn open_workspace(
  type PeerState (line 59) | struct PeerState {
  type WorkspaceDescriptor (line 64) | struct WorkspaceDescriptor {
  type AppService (line 68) | struct AppService {
    method new (line 388) | fn new(app: Rc<RefCell<App>>) -> Self {
    method state (line 393) | fn state(&self) -> ServiceState {
    type State (line 401) | type State = ServiceState;
    type Update (line 402) | type Update = ServiceState;
    type Request (line 403) | type Request = ServiceRequest;
    type Response (line 404) | type Response = Result<ServiceResponse, ServiceError>;
    method init (line 406) | fn init(&mut self, _connection: &server::Connection) -> Self::State {
    method poll_update (line 410) | fn poll_update(&mut self, _: &server::Connection) -> Async<Option<Self...
    method request (line 418) | fn request(
  type ServiceState (line 74) | pub struct ServiceState {
  type ServiceRequest (line 79) | pub enum ServiceRequest {
  type ServiceResponse (line 84) | pub enum ServiceResponse {
  type ServiceError (line 89) | pub enum ServiceError {
  type WorkspaceEntry (line 93) | enum WorkspaceEntry {
  type WorkspaceOpenError (line 98) | enum WorkspaceOpenError {
    method from (line 450) | fn from(error: rpc::Error) -> Self {
    method fmt (line 456) | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
  function test_remote_workspaces (line 475) | fn test_remote_workspaces() {
  function connect (line 514) | fn connect(reactor: &mut reactor::Core, server: Rc<RefCell<App>>, client...

FILE: xray_core/src/buffer.rs
  type ReplicaId (line 23) | pub type ReplicaId = usize;
  type LocalTimestamp (line 24) | type LocalTimestamp = usize;
  type LamportTimestamp (line 25) | type LamportTimestamp = usize;
  type SelectionSetId (line 26) | pub type SelectionSetId = usize;
  type SelectionSetVersion (line 27) | type SelectionSetVersion = usize;
  type BufferId (line 28) | pub type BufferId = usize;
  type Version (line 31) | pub struct Version(
    method new (line 236) | fn new() -> Self {
    method set (line 240) | fn set(&mut self, replica_id: ReplicaId, timestamp: LocalTimestamp) {
    method include (line 245) | fn include(&mut self, insertion: &Insertion) {
    method includes (line 251) | fn includes(&self, insertion: &Insertion) -> bool {
  type Error (line 37) | pub enum Error {
  type Buffer (line 46) | pub struct Buffer {
    method new (line 533) | pub fn new(id: BufferId) -> Self {
    method remote (line 584) | pub fn remote(
    method id (line 675) | pub fn id(&self) -> BufferId {
    method next_replica_id (line 679) | pub fn next_replica_id(&mut self) -> Result<ReplicaId, ()> {
    method file_id (line 685) | pub fn file_id(&self) -> Option<fs::FileId> {
    method set_file (line 689) | pub fn set_file(&mut self, file: Box<fs::File>) {
    method save (line 693) | pub fn save(&self) -> Option<Box<Future<Item = (), Error = Error>>> {
    method len (line 715) | pub fn len(&self) -> usize {
    method len_for_row (line 719) | pub fn len_for_row(&self, row: u32) -> Result<u32, Error> {
    method longest_row (line 730) | pub fn longest_row(&self) -> u32 {
    method max_point (line 734) | pub fn max_point(&self) -> Point {
    method line (line 738) | pub fn line(&self, row: u32) -> Result<Vec<u16>, Error> {
    method snapshot (line 747) | pub fn snapshot(&self) -> BufferSnapshot {
    method to_u16_chars (line 753) | pub fn to_u16_chars(&self) -> Vec<u16> {
    method to_string (line 760) | pub fn to_string(&self) -> String {
    method iter (line 764) | pub fn iter(&self) -> Iter {
    method iter_starting_at_point (line 768) | pub fn iter_starting_at_point(&self, point: Point) -> Iter {
    method backward_iter_starting_at_point (line 772) | pub fn backward_iter_starting_at_point(&self, point: Point) -> Backwar...
    method edit (line 776) | pub fn edit<'a, I, T>(&mut self, old_ranges: I, new_text: T) -> Vec<Ar...
    method add_selection_set (line 804) | pub fn add_selection_set(
    method remove_selection_set (line 827) | pub fn remove_selection_set(&mut self, id: SelectionSetId) -> Result<(...
    method selections (line 837) | pub fn selections(&self, set_id: SelectionSetId) -> Result<&[Selection...
    method insert_selections (line 844) | pub fn insert_selections<F>(&mut self, set_id: SelectionSetId, f: F) -...
    method mutate_selections (line 891) | pub fn mutate_selections<F>(&mut self, set_id: SelectionSetId, f: F) -...
    method merge_selections (line 910) | fn merge_selections(&mut self, selections: &mut Vec<Selection>) {
    method remote_selections (line 934) | pub fn remote_selections(&self) -> impl Iterator<Item = (UserId, &[Sel...
    method updates (line 947) | pub fn updates(&self) -> NotifyCellObserver<()> {
    method broadcast_op (line 951) | fn broadcast_op(&mut self, op: &Arc<Operation>) {
    method integrate_op (line 963) | fn integrate_op(&mut self, op: Arc<Operation>) -> Result<(), Error> {
    method integrate_edit (line 991) | fn integrate_edit(
    method update_remote_selection_set (line 1100) | fn update_remote_selection_set(
    method remove_remote_selection_set (line 1118) | fn remove_remote_selection_set(&mut self, replica_id: ReplicaId, set_i...
    method remove_remote_selection_sets (line 1123) | fn remove_remote_selection_sets(&mut self, id: ReplicaId) {
    method resolve_fragment_id (line 1129) | fn resolve_fragment_id(&self, edit_id: EditId, offset: usize) -> Resul...
    method outgoing_ops (line 1142) | fn outgoing_ops(&mut self) -> unsync::mpsc::UnboundedReceiver<Arc<Oper...
    method splice_fragments (line 1148) | fn splice_fragments<'a, I>(
    method split_fragment (line 1422) | fn split_fragment(
    method build_fragment_to_insert (line 1507) | fn build_fragment_to_insert(
    method anchor_before_offset (line 1542) | pub fn anchor_before_offset(&self, offset: usize) -> Result<Anchor, Er...
    method anchor_after_offset (line 1546) | pub fn anchor_after_offset(&self, offset: usize) -> Result<Anchor, Err...
    method anchor_for_offset (line 1550) | fn anchor_for_offset(&self, offset: usize, bias: AnchorBias) -> Result...
    method anchor_before_point (line 1589) | pub fn anchor_before_point(&self, point: Point) -> Result<Anchor, Erro...
    method anchor_after_point (line 1593) | pub fn anchor_after_point(&self, point: Point) -> Result<Anchor, Error> {
    method anchor_for_point (line 1597) | fn anchor_for_point(&self, point: Point, bias: AnchorBias) -> Result<A...
    method offset_for_anchor (line 1636) | pub fn offset_for_anchor(&self, anchor: &Anchor) -> Result<usize, Erro...
    method point_for_anchor (line 1640) | pub fn point_for_anchor(&self, anchor: &Anchor) -> Result<Point, Error> {
    method position_for_anchor (line 1644) | fn position_for_anchor(&self, anchor: &Anchor) -> Result<(usize, Point...
    method offset_for_point (line 1701) | fn offset_for_point(&self, point: Point) -> Result<usize, Error> {
    method cmp_anchors (line 1728) | pub fn cmp_anchors(&self, a: &Anchor, b: &Anchor) -> Result<Ordering, ...
    method cache_position (line 1734) | fn cache_position(&self, anchor: Option<Anchor>, offset: usize, point:...
  type BufferSnapshot (line 65) | pub struct BufferSnapshot {
    method iter (line 1748) | pub fn iter<'a>(&'a self) -> impl 'a + Iterator<Item = &'a [u16]> {
    method to_string (line 1760) | pub fn to_string(&self) -> String {
  type Point (line 70) | pub struct Point {
    method new (line 1766) | pub fn new(row: u32, column: u32) -> Self {
    method zero (line 1771) | pub fn zero() -> Self {
    method is_zero (line 1775) | pub fn is_zero(&self) -> bool {
    type Summary (line 1781) | type Summary = FragmentSummary;
    method from_summary (line 1783) | fn from_summary(summary: &Self::Summary) -> Self {
    type Output (line 1789) | type Output = Point;
    method add (line 1791) | fn add(self, other: &'a Self) -> Self::Output {
    type Output (line 1801) | type Output = Point;
    method sub (line 1803) | fn sub(self, other: &'a Self) -> Self::Output {
  type Anchor (line 76) | pub struct Anchor(AnchorInner);
  type AnchorInner (line 79) | enum AnchorInner {
  type AnchorBias (line 90) | enum AnchorBias {
  type Selection (line 96) | pub struct Selection {
    method head (line 1959) | pub fn head(&self) -> &Anchor {
    method set_head (line 1967) | pub fn set_head(&mut self, buffer: &Buffer, cursor: Anchor) {
    method tail (line 1983) | pub fn tail(&self) -> &Anchor {
    method is_empty (line 1991) | pub fn is_empty(&self, buffer: &Buffer) -> bool {
    method anchor_range (line 1995) | pub fn anchor_range(&self) -> Range<Anchor> {
  type SelectionSet (line 103) | struct SelectionSet {
    method state (line 1849) | fn state(&self) -> SelectionSetState {
  type SelectionSetState (line 110) | pub struct SelectionSetState {
  type Iter (line 115) | pub struct Iter<'a> {
  type BackwardIter (line 120) | pub struct BackwardIter<'a> {
  type Insertion (line 126) | pub struct Insertion {
  type Deletion (line 137) | pub struct Deletion {
  type Text (line 146) | pub struct Text {
    method new (line 2001) | fn new(code_units: Vec<u16>) -> Self {
    method len (line 2096) | fn len(&self) -> usize {
    method longest_row_in_range (line 2100) | fn longest_row_in_range(&self, target_range: Range<usize>) -> Result<(...
    method point_for_offset (line 2173) | fn point_for_offset(&self, offset: usize) -> Result<Point, Error> {
    method offset_for_point (line 2190) | fn offset_for_point(&self, point: Point) -> Result<usize, Error> {
    method search (line 2202) | fn search<F>(&self, mut f: F) -> Option<(Range<usize>, u32, &LineNode)>
    method from (line 2245) | fn from(s: &'a str) -> Self {
    method from (line 2251) | fn from(s: Vec<u16>) -> Self {
  type LineNode (line 152) | struct LineNode {
  type LineNodeProbe (line 160) | struct LineNodeProbe<'a> {
  type EditId (line 171) | pub struct EditId {
  type FragmentId (line 177) | struct FragmentId(
    method min_value (line 2267) | fn min_value() -> Self {
    method max_value (line 2271) | fn max_value() -> Self {
    method between (line 2275) | fn between(left: &Self, right: &Self) -> Self {
    method between_with_max (line 2279) | fn between_with_max(left: &Self, right: &Self, max_value: u16) -> Self {
    type Summary (line 2299) | type Summary = FragmentSummary;
    method from_summary (line 2301) | fn from_summary(summary: &Self::Summary) -> Self {
    type Output (line 2307) | type Output = FragmentId;
    method add (line 2309) | fn add(self, other: &'a Self) -> Self::Output {
  type Fragment (line 184) | struct Fragment {
    method new (line 2384) | fn new(id: FragmentId, insertion: Insertion) -> Self {
    method get_code_unit (line 2395) | fn get_code_unit(&self, offset: usize) -> Option<u16> {
    method len (line 2403) | fn len(&self) -> usize {
    method is_visible (line 2411) | fn is_visible(&self) -> bool {
    method point_for_offset (line 2415) | fn point_for_offset(&self, offset: usize) -> Result<Point, Error> {
    method offset_for_point (line 2424) | fn offset_for_point(&self, point: Point) -> Result<usize, Error> {
    type Summary (line 2432) | type Summary = FragmentSummary;
    method summarize (line 2434) | fn summarize(&self) -> Self::Summary {
  type FragmentSummary (line 193) | pub struct FragmentSummary {
    method add_assign (line 2480) | fn add_assign(&mut self, other: &Self) {
  type CharacterCount (line 203) | struct CharacterCount(usize);
    type Summary (line 2513) | type Summary = FragmentSummary;
    method from_summary (line 2515) | fn from_summary(summary: &Self::Summary) -> Self {
    type Output (line 2521) | type Output = CharacterCount;
    method add (line 2523) | fn add(self, other: &'a Self) -> Self::Output {
  type InsertionSplit (line 206) | struct InsertionSplit {
    type Summary (line 2535) | type Summary = InsertionSplitSummary;
    method summarize (line 2537) | fn summarize(&self) -> Self::Summary {
  type InsertionSplitSummary (line 212) | struct InsertionSplitSummary {
    method add_assign (line 2545) | fn add_assign(&mut self, other: &Self) {
  type InsertionOffset (line 217) | struct InsertionOffset(usize);
    type Summary (line 2557) | type Summary = InsertionSplitSummary;
    method from_summary (line 2559) | fn from_summary(summary: &Self::Summary) -> Self {
    type Output (line 2565) | type Output = InsertionOffset;
    method add (line 2567) | fn add(self, other: &'a Self) -> Self::Output {
  type Operation (line 220) | pub enum Operation {
    method replica_id (line 2579) | fn replica_id(&self) -> ReplicaId {
  type State (line 276) | pub struct State {
  type Request (line 287) | pub enum Request {
  type Response (line 298) | pub enum Response {
  type Update (line 304) | pub enum Update {
  type Fragment (line 316) | pub(super) struct Fragment {
    method new (line 2384) | fn new(id: FragmentId, insertion: Insertion) -> Self {
    method get_code_unit (line 2395) | fn get_code_unit(&self, offset: usize) -> Option<u16> {
    method len (line 2403) | fn len(&self) -> usize {
    method is_visible (line 2411) | fn is_visible(&self) -> bool {
    method point_for_offset (line 2415) | fn point_for_offset(&self, offset: usize) -> Result<Point, Error> {
    method offset_for_point (line 2424) | fn offset_for_point(&self, point: Point) -> Result<usize, Error> {
    type Summary (line 2432) | type Summary = FragmentSummary;
    method summarize (line 2434) | fn summarize(&self) -> Self::Summary {
  type Service (line 324) | pub struct Service {
    method new (line 333) | pub fn new(buffer: Rc<RefCell<Buffer>>) -> Self {
    method poll_outgoing_op (line 358) | fn poll_outgoing_op(&mut self) -> Async<Option<Update>> {
    method poll_outgoing_selection_updates (line 365) | fn poll_outgoing_selection_updates(&mut self) -> Async<Option<Update>> {
    type State (line 414) | type State = State;
    type Update (line 415) | type Update = Update;
    type Request (line 416) | type Request = Request;
    type Response (line 417) | type Response = Response;
    method init (line 419) | fn init(&mut self, _: &rpc::server::Connection) -> Self::State {
    method poll_update (line 460) | fn poll_update(&mut self, _: &rpc::server::Connection) -> Async<Option...
    method request (line 475) | fn request(
  method drop (line 514) | fn drop(&mut self) {
  function serialize_op (line 521) | fn serialize_op<S: Serializer>(op: &Arc<Operation>, serializer: S) -> Re...
  function deserialize_op (line 525) | fn deserialize_op<'de, D: Deserializer<'de>>(
  method add_assign (line 1815) | fn add_assign(&mut self, other: Self) {
  method partial_cmp (line 1826) | fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
  method cmp (line 1833) | fn cmp(&self, other: &Point) -> Ordering {
  method cmp (line 1840) | fn cmp(&self, other: &Point) -> Ordering {
  function new (line 1858) | fn new(buffer: &'a Buffer) -> Self {
  function starting_at_point (line 1867) | fn starting_at_point(buffer: &'a Buffer, point: Point) -> Self {
  type Item (line 1885) | type Item = u16;
  method next (line 1887) | fn next(&mut self) -> Option<Self::Item> {
  function starting_at_point (line 1912) | fn starting_at_point(buffer: &'a Buffer, point: Point) -> Self {
  type Item (line 1930) | type Item = u16;
  method next (line 1932) | fn next(&mut self) -> Option<Self::Item> {
  function log2_fast (line 2257) | fn log2_fast(x: usize) -> usize {
  method add_assign (line 2315) | fn add_assign(&mut self, other: Self) {
  function serialize_option_arc (line 2322) | fn serialize_option_arc<T, S>(option: &Option<Arc<T>>, serializer: S) ->...
  function deserialize_option_arc (line 2334) | fn deserialize_option_arc<'de, T, D>(deserializer: D) -> Result<Option<A...
  function serialize_arc (line 2367) | fn serialize_arc<T, S>(arc: &Arc<T>, serializer: S) -> Result<S::Ok, S::...
  function deserialize_arc (line 2375) | fn deserialize_arc<'de, T, D>(deserializer: D) -> Result<Arc<T>, D::Error>
  method default (line 2500) | fn default() -> Self {
  method add_assign (line 2529) | fn add_assign(&mut self, other: Self) {
  method default (line 2551) | fn default() -> Self {
  method add_assign (line 2573) | fn add_assign(&mut self, other: Self) {
  function should_insert_before (line 2586) | fn should_insert_before(
  function test_edit (line 2610) | fn test_edit() {
  function test_random_edits (line 2627) | fn test_random_edits() {
  function test_len_for_row (line 2664) | fn test_len_for_row() {
  function test_longest_row (line 2681) | fn test_longest_row() {
  function iter_starting_at_point (line 2697) | fn iter_starting_at_point() {
  function backward_iter_starting_at_point (line 2750) | fn backward_iter_starting_at_point() {
  function test_point_for_offset (line 2789) | fn test_point_for_offset() {
  function test_offset_for_point (line 2815) | fn test_offset_for_point() {
  function test_longest_row_in_range (line 2845) | fn test_longest_row_in_range() {
  function fragment_ids (line 2888) | fn fragment_ids() {
  function test_anchors (line 2909) | fn test_anchors() {
  function anchors_at_start_and_end (line 3051) | fn anchors_at_start_and_end() {
  function test_snapshot (line 3074) | fn test_snapshot() {
  function test_random_concurrent_edits (line 3090) | fn test_random_concurrent_edits() {
  function test_edit_replication (line 3151) | fn test_edit_replication() {
  function test_selection_replication (line 3201) | fn test_selection_replication() {
  type RandomCharIter (line 3303) | struct RandomCharIter<T: Rng>(T);
  type Item (line 3306) | type Item = char;
  method next (line 3308) | fn next(&mut self) -> Option<Self::Item> {
  function selections (line 3317) | fn selections(buffer: &Rc<RefCell<Buffer>>) -> Vec<(ReplicaId, Selection...
  function empty_selection (line 3334) | fn empty_selection(buffer: &Buffer, offset: usize) -> Selection {

FILE: xray_core/src/buffer_view.rs
  type BufferViewDelegate (line 13) | pub trait BufferViewDelegate {
    method set_active_buffer_view (line 14) | fn set_active_buffer_view(&mut self, buffer_view: WeakViewHandle<Buffe...
  type BufferView (line 17) | pub struct BufferView {
    method new (line 96) | pub fn new(
    method set_height (line 137) | pub fn set_height(&mut self, height: f64) -> &mut Self {
    method set_width (line 145) | pub fn set_width(&mut self, width: f64) -> &mut Self {
    method set_line_height (line 153) | pub fn set_line_height(&mut self, line_height: f64) -> &mut Self {
    method set_scroll_top (line 161) | pub fn set_scroll_top(&mut self, scroll_top: f64) -> &mut Self {
    method scroll_top (line 170) | fn scroll_top(&self) -> f64 {
    method scroll_bottom (line 175) | fn scroll_bottom(&self) -> f64 {
    method save (line 179) | pub fn save(&self) -> Option<Box<Future<Item = (), Error = buffer::Err...
    method edit (line 183) | pub fn edit(&mut self, text: &str) {
    method backspace (line 228) | pub fn backspace(&mut self) {
    method delete (line 235) | pub fn delete(&mut self) {
    method all_selections_are_empty (line 242) | fn all_selections_are_empty(&self) -> bool {
    method set_selected_anchor_range (line 249) | pub fn set_selected_anchor_range(
    method set_cursor_position (line 273) | pub fn set_cursor_position(&mut self, position: Point, autoscroll: boo...
    method add_selection (line 293) | pub fn add_selection(&mut self, start: Point, end: Point) {
    method add_selection_above (line 323) | pub fn add_selection_above(&mut self) {
    method add_selection_below (line 375) | pub fn add_selection_below(&mut self) {
    method move_left (line 429) | pub fn move_left(&mut self) {
    method select_left (line 454) | pub fn select_left(&mut self) {
    method move_right (line 471) | pub fn move_right(&mut self) {
    method select_right (line 496) | pub fn select_right(&mut self) {
    method move_up (line 513) | pub fn move_up(&mut self) {
    method select_up (line 536) | pub fn select_up(&mut self) {
    method move_down (line 551) | pub fn move_down(&mut self) {
    method select_down (line 574) | pub fn select_down(&mut self) {
    method move_to_beginning_of_word (line 589) | pub fn move_to_beginning_of_word(&mut self) {
    method move_to_end_of_word (line 607) | pub fn move_to_end_of_word(&mut self) {
    method select_to_beginning_of_word (line 625) | pub fn select_to_beginning_of_word(&mut self) {
    method select_to_end_of_word (line 641) | pub fn select_to_end_of_word(&mut self) {
    method select_word (line 657) | pub fn select_word(&mut self) {
    method move_to_beginning_of_line (line 674) | pub fn move_to_beginning_of_line(&mut self) {
    method move_to_end_of_line (line 692) | pub fn move_to_end_of_line(&mut self) {
    method select_to_beginning_of_line (line 710) | pub fn select_to_beginning_of_line(&mut self) {
    method select_to_end_of_line (line 726) | pub fn select_to_end_of_line(&mut self) {
    method select_line (line 742) | pub fn select_line(&mut self) {
    method move_to_top (line 760) | pub fn move_to_top(&mut self) {
    method move_to_bottom (line 776) | pub fn move_to_bottom(&mut self) {
    method select_to_top (line 792) | pub fn select_to_top(&mut self) {
    method select_to_bottom (line 806) | pub fn select_to_bottom(&mut self) {
    method selections (line 820) | pub fn selections(&self) -> Ref<[Selection]> {
    method buffer_id (line 826) | pub fn buffer_id(&self) -> BufferId {
    method render_selections (line 830) | fn render_selections(&self, range: Range<Point>) -> Vec<SelectionProps> {
    method query_selections (line 861) | fn query_selections<'a>(
    method autoscroll_to_cursor (line 900) | fn autoscroll_to_cursor(&mut self, center: bool) {
    method autoscroll_to_selection (line 915) | fn autoscroll_to_selection(&mut self, center: bool) {
    method flush_vertical_autoscroll_to_selection (line 925) | fn flush_vertical_autoscroll_to_selection(&mut self) {
    method autoscroll_to_range (line 932) | fn autoscroll_to_range(
    method updated (line 974) | fn updated(&mut self) {
  type SelectionProps (line 36) | struct SelectionProps {
  type BufferViewAction (line 46) | enum BufferViewAction {
  type AutoScrollRequest (line 90) | struct AutoScrollRequest {
  method component_name (line 980) | fn component_name(&self) -> &'static str {
  method will_mount (line 984) | fn will_mount(&mut self, window: &mut Window, self_handle: WeakViewHandl...
  method render (line 992) | fn render(&self) -> serde_json::Value {
  method dispatch_action (line 1060) | fn dispatch_action(&mut self, action: serde_json::Value, _: &mut Window) {
  type Item (line 1111) | type Item = ();
  type Error (line 1112) | type Error = ();
  method poll (line 1114) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  method drop (line 1120) | fn drop(&mut self) {
  function test_cursor_movement (line 1135) | fn test_cursor_movement() {
  function test_selection_movement (line 1216) | fn test_selection_movement() {
  function test_move_to_beginning_or_end_of_word (line 1307) | fn test_move_to_beginning_or_end_of_word() {
  function test_select_to_beginning_or_end_of_word (line 1348) | fn test_select_to_beginning_or_end_of_word() {
  function test_move_to_beginning_or_end_of_line (line 1416) | fn test_move_to_beginning_or_end_of_line() {
  function test_select_to_beginning_or_end_of_line (line 1442) | fn test_select_to_beginning_or_end_of_line() {
  function test_select_word (line 1480) | fn test_select_word() {
  function test_select_line (line 1494) | fn test_select_line() {
  function test_move_to_top_or_bottom (line 1508) | fn test_move_to_top_or_bottom() {
  function test_select_to_top_or_bottom (line 1520) | fn test_select_to_top_or_bottom() {
  function test_backspace (line 1537) | fn test_backspace() {
  function test_delete (line 1549) | fn test_delete() {
  function test_add_selection (line 1561) | fn test_add_selection() {
  function test_add_selection_above (line 1615) | fn test_add_selection_above() {
  function test_add_selection_below (line 1684) | fn test_add_selection_below() {
  function test_set_cursor_position (line 1744) | fn test_set_cursor_position() {
  function test_edit (line 1763) | fn test_edit() {
  function test_autoscroll (line 1806) | fn test_autoscroll() {
  function test_render (line 1835) | fn test_render() {
  function test_render_past_last_line (line 1907) | fn test_render_past_last_line() {
  function test_dropping_view_removes_selection_set (line 1930) | fn test_dropping_view_removes_selection_set() {
  function stringify_lines (line 1940) | fn stringify_lines(lines: &serde_json::Value) -> Vec<String> {
  function render_selections (line 1949) | fn render_selections(editor: &BufferView) -> Vec<SelectionProps> {
  function empty_selection (line 1964) | fn empty_selection(row: u32, column: u32) -> SelectionProps {
  function selection (line 1974) | fn selection(start: (u32, u32), end: (u32, u32)) -> SelectionProps {
  function rev_selection (line 1984) | fn rev_selection(start: (u32, u32), end: (u32, u32)) -> SelectionProps {

FILE: xray_core/src/cross_platform.rs
  constant UNIX_MAIN_SEPARATOR (line 7) | pub const UNIX_MAIN_SEPARATOR: u8 = b'/';
  type PathComponent (line 10) | pub enum PathComponent {
    method to_string_lossy (line 23) | pub fn to_string_lossy(&self) -> Cow<str> {
    method from (line 106) | fn from(string: OsString) -> Self {
    method from (line 114) | fn from(string: &'a OsStr) -> Self {
    method from (line 123) | fn from(string: &'a str) -> Self {
  type Path (line 15) | pub struct Path(Option<PathState>);
    method new (line 31) | pub fn new() -> Self {
    method push (line 35) | pub fn push(&mut self, component: &PathComponent) {
    method push_path (line 53) | pub fn push_path(&mut self, other: &Self) {
    method to_path_buf (line 72) | pub fn to_path_buf(&self) -> PathBuf {
    method to_string_lossy (line 85) | pub fn to_string_lossy(&self) -> String {
    method from (line 98) | fn from(path: OsString) -> Self {
    method from (line 131) | fn from(string: &'a str) -> Self {
  type PathState (line 18) | enum PathState {

FILE: xray_core/src/file_finder.rs
  type FileFinderViewDelegate (line 8) | pub trait FileFinderViewDelegate {
    method search_paths (line 9) | fn search_paths(
    method did_close (line 15) | fn did_close(&mut self);
    method did_confirm (line 16) | fn did_confirm(
  type FileFinderView (line 24) | pub struct FileFinderView<T: FileFinderViewDelegate> {
  type FileFinderAction (line 36) | enum FileFinderAction {
  method component_name (line 46) | fn component_name(&self) -> &'static str {
  method render (line 50) | fn render(&self) -> serde_json::Value {
  method dispatch_action (line 58) | fn dispatch_action(&mut self, action: serde_json::Value, window: &mut Wi...
  type Item (line 74) | type Item = ();
  type Error (line 75) | type Error = ();
  method poll (line 77) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  function new (line 101) | pub fn new(delegate: WeakViewHandle<T>) -> Self {
  function update_query (line 113) | fn update_query(&mut self, query: String, window: &mut Window) {
  function update_include_ignored (line 121) | fn update_include_ignored(&mut self, include_ignored: bool, window: &mut...
  function select_previous (line 129) | fn select_previous(&mut self) {
  function select_next (line 136) | fn select_next(&mut self) {
  function confirm (line 143) | fn confirm(&mut self, window: &mut Window) {
  function close (line 151) | fn close(&mut self) {
  function search (line 155) | fn search(&mut self, window: &mut Window) {

FILE: xray_core/src/fs.rs
  type EntryId (line 17) | pub type EntryId = usize;
  type FileId (line 18) | pub type FileId = u64;
  type Tree (line 20) | pub trait Tree {
    method root (line 21) | fn root(&self) -> Entry;
    method updates (line 22) | fn updates(&self) -> Box<Stream<Item = (), Error = ()>>;
    method root (line 284) | fn root(&self) -> Entry {
    method updates (line 288) | fn updates(&self) -> Box<Stream<Item = (), Error = ()>> {
    method root (line 423) | fn root(&self) -> Entry {
    method updates (line 427) | fn updates(&self) -> Box<Stream<Item = (), Error = ()>> {
  type LocalTree (line 25) | pub trait LocalTree: Tree {
    method path (line 26) | fn path(&self) -> &cross_platform::Path;
    method populated (line 27) | fn populated(&self) -> Box<Future<Item = (), Error = ()>>;
    method as_tree (line 28) | fn as_tree(&self) -> &Tree;
    method path (line 433) | fn path(&self) -> &cross_platform::Path {
    method populated (line 437) | fn populated(&self) -> Box<Future<Item = (), Error = ()>> {
    method as_tree (line 451) | fn as_tree(&self) -> &Tree {
  type FileProvider (line 31) | pub trait FileProvider {
    method open (line 32) | fn open(&self, path: &cross_platform::Path)
    method open (line 518) | fn open(
  type File (line 36) | pub trait File {
    method id (line 37) | fn id(&self) -> FileId;
    method read (line 38) | fn read(&self) -> Box<Future<Item = String, Error = io::Error>>;
    method write_snapshot (line 39) | fn write_snapshot(&self, snapshot: BufferSnapshot)
    method id (line 537) | fn id(&self) -> FileId {
    method read (line 541) | fn read(&self) -> Box<Future<Item = String, Error = io::Error>> {
    method write_snapshot (line 549) | fn write_snapshot(
  type Entry (line 44) | pub enum Entry {
    method file (line 86) | pub fn file(name: cross_platform::PathComponent, symlink: bool, ignore...
    method dir (line 95) | pub fn dir(name: cross_platform::PathComponent, symlink: bool, ignored...
    method is_dir (line 107) | pub fn is_dir(&self) -> bool {
    method id (line 114) | pub fn id(&self) -> EntryId {
    method name (line 121) | pub fn name(&self) -> &cross_platform::PathComponent {
    method name_chars (line 128) | pub fn name_chars(&self) -> &[char] {
    method is_symlink (line 135) | pub fn is_symlink(&self) -> bool {
    method is_ignored (line 142) | pub fn is_ignored(&self) -> bool {
    method children (line 149) | pub fn children(&self) -> Option<Arc<Vec<Entry>>> {
    method insert (line 156) | pub fn insert(&self, new_entry: Entry) -> Result<(), ()> {
    method from_json (line 457) | fn from_json<T: Into<PathComponent>>(name: T, json: &serde_json::Value...
    method child_names (line 471) | fn child_names(&self) -> Vec<String> {
  type DirInner (line 52) | pub struct DirInner {
  type FileInner (line 64) | pub struct FileInner {
  type TreeService (line 72) | pub struct TreeService {
    method new (line 225) | pub fn new(tree: Rc<LocalTree>) -> Self {
    type State (line 232) | type State = Entry;
    type Update (line 233) | type Update = Entry;
    type Request (line 234) | type Request = ();
    type Response (line 235) | type Response = ();
    method init (line 237) | fn init(&mut self, connection: &server::Connection) -> Self::State {
    method poll_update (line 246) | fn poll_update(&mut self, _: &server::Connection) -> Async<Option<Self...
  type RemoteTree (line 77) | pub struct RemoteTree(Rc<RefCell<RemoteTreeState>>);
    method new (line 261) | pub fn new(foreground: ForegroundExecutor, service: client::Service<Tr...
  type RemoteTreeState (line 79) | struct RemoteTreeState {
  function serialize_dir (line 185) | fn serialize_dir<S: Serializer>(dir: &Arc<DirInner>, serializer: S) -> R...
  function deserialize_dir (line 189) | fn deserialize_dir<'de, D: Deserializer<'de>>(deserializer: D) -> Result...
  function serialize_file (line 199) | fn serialize_file<S: Serializer>(file: &Arc<FileInner>, serializer: S) -...
  function deserialize_file (line 203) | fn deserialize_file<'de, D: Deserializer<'de>>(
  function serialize_dir_children (line 211) | fn serialize_dir_children<S: Serializer>(
  function deserialize_dir_children (line 218) | fn deserialize_dir_children<'de, D: Deserializer<'de>>(
  function test_insert (line 309) | fn test_insert() {
  function test_serialize_deserialize (line 331) | fn test_serialize_deserialize() {
  function test_tree_replication (line 354) | fn test_tree_replication() {
  type TestTree (line 383) | pub struct TestTree {
    method new (line 407) | pub fn new<T: Into<OsString>>(path: T, root: Entry) -> Self {
    method from_json (line 415) | pub fn from_json<T: Into<PathBuf>>(path: T, json: serde_json::Value) -...
  type TestFileProvider (line 389) | pub struct TestFileProvider(Rc<RefCell<TestFileProviderState>>);
    method new (line 494) | pub fn new() -> Self {
    method write_sync (line 501) | pub fn write_sync<S: Into<String>>(&self, path: cross_platform::Path, ...
  type TestFileProviderState (line 391) | struct TestFileProviderState {
  type TestFile (line 397) | struct TestFile(Rc<RefCell<TestFileState>>);
  type TestFileState (line 399) | struct TestFileState {
  type NextTick (line 404) | struct NextTick(bool);
    method new (line 563) | fn new() -> Self {
  method eq (line 485) | fn eq(&self, other: &Self) -> bool {
  type Item (line 569) | type Item = ();
  type Error (line 570) | type Error = Never;
  method poll (line 572) | fn poll(&mut self) -> Poll<Self::Item, Self::Error> {

FILE: xray_core/src/fuzzy.rs
  type Score (line 5) | pub type Score = f64;
  constant SCORE_MIN (line 7) | pub const SCORE_MIN: Score = f64::NEG_INFINITY;
  constant SCORE_GAP_LEADING (line 8) | const SCORE_GAP_LEADING: Score = -0.005;
  constant SCORE_GAP_TRAILING (line 9) | const SCORE_GAP_TRAILING: Score = -0.005;
  constant SCORE_GAP_INNER (line 10) | const SCORE_GAP_INNER: Score = -0.01;
  constant SCORE_MATCH_CONSECUTIVE (line 11) | const SCORE_MATCH_CONSECUTIVE: Score = 1.0;
  constant SCORE_MATCH_SLASH (line 12) | const SCORE_MATCH_SLASH: Score = 0.9;
  constant SCORE_MATCH_WORD (line 13) | const SCORE_MATCH_WORD: Score = 0.8;
  constant SCORE_MATCH_CAPITAL (line 14) | const SCORE_MATCH_CAPITAL: Score = 0.7;
  constant SCORE_MATCH_DOT (line 15) | const SCORE_MATCH_DOT: Score = 0.6;
  type Matcher (line 17) | pub struct Matcher<'a> {
  type Scorer (line 22) | pub struct Scorer<'a> {
  type Matrix (line 30) | struct Matrix<T> {
  function new (line 37) | pub fn new(needle: &'a [char]) -> Self {
  function push (line 44) | pub fn push(&mut self, component: &[char]) -> bool {
  function pop (line 63) | pub fn pop(&mut self) {
  function new (line 69) | pub fn new(needle: &'a [char]) -> Self {
  function push (line 79) | pub fn push(&mut self, component: &[char], positions: Option<&mut [usize...
  function pop (line 155) | pub fn pop(&mut self) {
  function precompute_bonus (line 161) | fn precompute_bonus(&mut self, component: &[char]) {
  function new (line 172) | pub fn new(rows: usize, cols: usize) -> Self {
  function add_columns (line 180) | fn add_columns(&mut self, additional: usize) {
  function remove_columns (line 187) | fn remove_columns(&mut self, exceeding: usize) {
  type Output (line 195) | type Output = T;
  function index (line 197) | fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
  type Output (line 203) | type Output = T;
  function index (line 205) | fn index(&self, (row, col): (usize, isize)) -> &Self::Output {
  function index_mut (line 212) | fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
  function fmt (line 218) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  function compute_bonus (line 231) | fn compute_bonus(last_ch: char, ch: char) -> Score {
  function test_matcher (line 284) | fn test_matcher() {
  function test_scorer (line 295) | fn test_scorer() {
  function to_chars (line 317) | fn to_chars(s: &str) -> Vec<char> {

FILE: xray_core/src/lib.rs
  type ForegroundExecutor (line 53) | pub type ForegroundExecutor = Rc<Executor<Box<Future<Item = (), Error = ...
  type BackgroundExecutor (line 54) | pub type BackgroundExecutor = Rc<Executor<Box<Future<Item = (), Error = ...
  type UserId (line 55) | pub type UserId = usize;
  type IntoShared (line 57) | pub(crate) trait IntoShared {
    method into_shared (line 58) | fn into_shared(self) -> Rc<RefCell<Self>>;
    method into_shared (line 62) | fn into_shared(self) -> Rc<RefCell<Self>> {

FILE: xray_core/src/movement.rs
  function left (line 5) | pub fn left(buffer: &Buffer, mut point: Point) -> Point {
  function right (line 15) | pub fn right(buffer: &Buffer, mut point: Point) -> Point {
  function up (line 26) | pub fn up(buffer: &Buffer, mut point: Point, goal_column: Option<u32>) -...
  function down (line 38) | pub fn down(buffer: &Buffer, mut point: Point, goal_column: Option<u32>)...
  function beginning_of_word (line 51) | pub fn beginning_of_word(buffer: &Buffer, mut point: Point) -> Point {
  function end_of_word (line 66) | pub fn end_of_word(buffer: &Buffer, mut point: Point) -> Point {
  function beginning_of_line (line 81) | pub fn beginning_of_line(mut point: Point) -> Point {
  function end_of_line (line 86) | pub fn end_of_line(buffer: &Buffer, mut point: Point) -> Point {

FILE: xray_core/src/never.rs
  type Never (line 2) | pub enum Never {}

FILE: xray_core/src/notify_cell.rs
  type Version (line 6) | type Version = usize;
  type TrySetError (line 9) | pub enum TrySetError {
  type NotifyCell (line 14) | pub struct NotifyCell<T: Clone> {
  type WeakNotifyCell (line 19) | pub struct WeakNotifyCell<T: Clone>(Weak<RwLock<Inner<T>>>);
  type NotifyCellObserver (line 22) | pub struct NotifyCellObserver<T: Clone> {
  type Inner (line 28) | struct Inner<T: Clone> {
  function new (line 36) | pub fn new(value: T) -> Self {
  function weak (line 48) | pub fn weak(value: T) -> (WeakNotifyCell<T>, NotifyCellObserver<T>) {
  function set (line 62) | pub fn set(&self, value: T) {
  function get (line 71) | pub fn get(&self) -> T {
  function observe (line 75) | pub fn observe(&self) -> NotifyCellObserver<T> {
  function has_observers (line 85) | pub fn has_observers(&self) -> bool {
  function try_set (line 89) | pub fn try_set(&self, value: T) -> Result<(), TrySetError> {
  function get (line 102) | pub fn get(&self) -> T {
  type Item (line 108) | type Item = T;
  type Error (line 109) | type Error = ();
  method poll (line 111) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  type Item (line 127) | type Item = T;
  type Error (line 128) | type Error = ();
  method poll (line 130) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  method drop (line 149) | fn drop(&mut self) {
  function test_notify (line 170) | fn test_notify() {
  function test_notify_cell_poll (line 205) | fn test_notify_cell_poll() {
  function test_weak_notify_cell (line 228) | fn test_weak_notify_cell() {

FILE: xray_core/src/project.rs
  type TreeId (line 19) | pub type TreeId = usize;
  type Project (line 21) | pub trait Project {
    method open_path (line 22) | fn open_path(
    method open_buffer (line 27) | fn open_buffer(
    method search_paths (line 31) | fn search_paths(
    method open_path (line 211) | fn open_path(
    method open_buffer (line 250) | fn open_buffer(
    method search_paths (line 264) | fn search_paths(
    method open_path (line 316) | fn open_path(
    method open_buffer (line 350) | fn open_buffer(
    method search_paths (line 379) | fn search_paths(
  type BufferWeakSet (line 39) | struct BufferWeakSet {
    method new (line 130) | fn new() -> Self {
    method insert (line 136) | fn insert(&mut self, buffer: Buffer) -> Rc<RefCell<Buffer>> {
    method find_by_buffer_id (line 142) | fn find_by_buffer_id(&mut self, buffer_id: BufferId) -> Option<Rc<RefC...
    method find_by_file_id (line 157) | fn find_by_file_id(&mut self, file_id: fs::FileId) -> Option<Rc<RefCel...
  type LocalProject (line 43) | pub struct LocalProject {
    method new (line 174) | pub fn new<T>(file_provider: Rc<fs::FileProvider>, trees: Vec<T>) -> Self
    method add_tree (line 191) | fn add_tree<T: 'static + fs::LocalTree>(&mut self, tree: T) {
    method resolve_path (line 197) | fn resolve_path(
  type RemoteProject (line 51) | pub struct RemoteProject {
    method new (line 294) | pub fn new(
  type ProjectService (line 57) | pub struct ProjectService {
    method new (line 409) | pub fn new(project: Rc<RefCell<LocalProject>>) -> Self {
    type State (line 418) | type State = RpcState;
    type Update (line 419) | type Update = RpcState;
    type Request (line 420) | type Request = RpcRequest;
    type Response (line 421) | type Response = RpcResponse;
    method init (line 423) | fn init(&mut self, connection: &rpc::server::Connection) -> Self::State {
    method poll_update (line 436) | fn poll_update(
    method request (line 443) | fn request(
  type RpcState (line 63) | pub struct RpcState {
  type RpcRequest (line 68) | pub enum RpcRequest {
  type RpcResponse (line 79) | pub enum RpcResponse {
  type PathSearch (line 83) | pub struct PathSearch {
    method find_matches (line 484) | fn find_matches(&mut self) -> Result<HashMap<fs::EntryId, MatchMarker>...
    method rank_matches (line 545) | fn rank_matches(
    method check_cancellation (line 664) | fn check_cancellation(
  type PathSearchStatus (line 94) | pub enum PathSearchStatus {
    method unwrap (line 967) | fn unwrap(self) -> Vec<PathSearchResult> {
  type PathSearchResult (line 100) | pub struct PathSearchResult {
  type StackEntry (line 108) | struct StackEntry {
  type MatchMarker (line 115) | enum MatchMarker {
  type Error (line 121) | pub enum Error {
    method from (line 714) | fn from(error: io::Error) -> Self {
    method from (line 720) | fn from(error: rpc::Error) -> Self {
  type Item (line 682) | type Item = ();
  type Error (line 683) | type Error = ();
  method poll (line 685) | fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
  method cmp (line 698) | fn cmp(&self, other: &Self) -> cmp::Ordering {
  method partial_cmp (line 704) | fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
  function test_open_same_path_concurrently (line 733) | fn test_open_same_path_concurrently() {
  function test_drop_buffer_rc (line 751) | fn test_drop_buffer_rc() {
  function test_search_one_tree (line 778) | fn test_search_one_tree() {
  function test_search_many_trees (line 827) | fn test_search_many_trees() {
  function test_replication (line 864) | fn test_replication() {
  function build_project (line 914) | fn build_project(file_provider: Rc<TestFileProvider>) -> LocalProject {
  function summarize_results (line 945) | fn summarize_results(

FILE: xray_core/src/rpc/client.rs
  type Service (line 14) | pub struct Service<T: server::Service> {
  type ServiceRegistration (line 19) | pub struct ServiceRegistration {
    method connection (line 352) | fn connection(&self) -> Result<Rc<RefCell<ConnectionState>>, Error> {
  type FullUpdateService (line 24) | pub struct FullUpdateService<T: server::Service> {
  type ServiceState (line 29) | struct ServiceState {
  type Connection (line 37) | pub struct Connection(Rc<RefCell<ConnectionState>>);
    method new (line 180) | pub fn new<S, B>(incoming: S) -> Box<Future<Item = (Self, Service<B>),...
    method service (line 206) | fn service<S: server::Service>(
    method update (line 230) | fn update(&mut self, message: MessageToClient) {
    method process_insertions (line 246) | fn process_insertions(&self, insertions: HashMap<ServiceId, Bytes>) {
    method process_updates (line 263) | fn process_updates(&self, updates: HashMap<ServiceId, Vec<Bytes>>) {
    method process_removals (line 277) | fn process_removals(&self, removals: HashSet<ServiceId>) {
    method process_responses (line 284) | fn process_responses(&self, responses: HashMap<ServiceId, Vec<(Request...
  type ConnectionState (line 39) | struct ConnectionState {
  function state (line 48) | pub fn state(&self) -> Result<T::State, Error> {
  function updates (line 58) | pub fn updates(&self) -> Result<Box<Stream<Item = T::Update, Error = ()>...
  function request (line 70) | pub fn request(&self, request: T::Request) -> Box<Future<Item = T::Respo...
  function take_service (line 108) | pub fn take_service<S: server::Service>(&self, id: ServiceId) -> Result<...
  method clone (line 116) | fn clone(&self) -> Self {
  function new (line 129) | pub fn new(service: Service<S>) -> Self {
  function latest_state (line 136) | pub fn latest_state(&self) -> Result<Ref<T>, Error> {
  function updates (line 145) | pub fn updates(&self) -> Result<Box<Stream<Item = (), Error = ()>>, Erro...
  function request (line 160) | pub fn request(&self, request: S::Request) -> Box<Future<Item = S::Respo...
  function take_service (line 164) | pub fn take_service<S2: server::Service>(&self, id: ServiceId) -> Result...
  method clone (line 171) | fn clone(&self) -> Self {
  type Item (line 309) | type Item = Bytes;
  type Error (line 310) | type Error = ();
  method poll (line 312) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  method drop (line 358) | fn drop(&mut self) {

FILE: xray_core/src/rpc/messages.rs
  type RequestId (line 5) | pub type RequestId = usize;
  type ServiceId (line 6) | pub type ServiceId = usize;
  type MessageToClient (line 9) | pub enum MessageToClient {
  type Response (line 18) | pub type Response = Result<Bytes, Error>;
  type MessageToServer (line 21) | pub enum MessageToServer {

FILE: xray_core/src/rpc/mod.rs
  type Error (line 10) | pub enum Error {
    method fmt (line 20) | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
    method description (line 31) | fn description(&self) -> &str {
  function test_connection (line 54) | fn test_connection() {
  function test_create_and_drop_service (line 79) | fn test_create_and_drop_service() {
  function test_creating_service_in_async_response (line 121) | fn test_creating_service_in_async_response() {
  function test_add_service_on_init_or_update (line 139) | fn test_add_service_on_init_or_update() {
  function test_drop_client_updates (line 197) | fn test_drop_client_updates() {
  function test_interrupting_connection_to_client (line 210) | fn test_interrupting_connection_to_client() {
  function test_interrupting_connection_to_server_during_handshake (line 220) | fn test_interrupting_connection_to_server_during_handshake() {
  function test_interrupting_connection_to_server_after_handshake (line 230) | fn test_interrupting_connection_to_server_after_handshake() {
  function connect (line 253) | pub fn connect<S: 'static + server::Service>(
  function connect_throttled (line 260) | fn connect_throttled<S: 'static + server::Service>(
  type TestModel (line 297) | struct TestModel {
    method new (line 397) | fn new(count: usize) -> Self {
    method increment_by (line 404) | fn increment_by(&self, delta: usize) {
  type TestService (line 302) | struct TestService {
    method new (line 323) | fn new(model: Rc<TestModel>) -> Self {
    type State (line 334) | type State = usize;
    type Update (line 335) | type Update = usize;
    type Request (line 336) | type Request = TestRequest;
    type Response (line 337) | type Response = TestServiceResponse;
    method init (line 339) | fn init(&mut self, _: &server::Connection) -> Self::State {
    method poll_update (line 343) | fn poll_update(&mut self, _: &server::Connection) -> Async<Option<Self...
    method request (line 347) | fn request(
  type TestRequest (line 309) | enum TestRequest {
  type TestServiceResponse (line 317) | enum TestServiceResponse {

FILE: xray_core/src/rpc/server.rs
  type Service (line 16) | pub trait Service {
    method init (line 22) | fn init(&mut self, connection: &Connection) -> Self::State;
    method poll_update (line 24) | fn poll_update(&mut self, _connection: &Connection) -> Async<Option<Se...
    method request (line 28) | fn request(
  type RawBytesService (line 37) | trait RawBytesService {
    method init (line 38) | fn init(&mut self, connection: &mut Connection) -> Bytes;
    method poll_update (line 39) | fn poll_update(&mut self, connection: &mut Connection) -> Async<Option...
    method request (line 40) | fn request(
    method init (line 284) | fn init(&mut self, connection: &mut Connection) -> Bytes {
    method poll_update (line 288) | fn poll_update(&mut self, connection: &mut Connection) -> Async<Option...
    method request (line 293) | fn request(
  type Connection (line 48) | pub struct Connection(Rc<RefCell<ConnectionState>>);
    method new (line 77) | pub fn new<S, T>(incoming: S, root_service: T) -> Self
    method add_service (line 96) | pub fn add_service<T: 'static + Service>(&self, service: T) -> Service...
    method poll_incoming (line 117) | fn poll_incoming(&mut self) -> Result<bool, io::Error> {
    method poll_outgoing (line 170) | fn poll_outgoing(&mut self) -> Poll<Option<Bytes>, ()> {
    method take_service (line 238) | fn take_service(&self, id: ServiceId) -> Option<Rc<RefCell<RawBytesSer...
  type ConnectionState (line 50) | struct ConnectionState {
  type ServiceRegistration (line 62) | struct ServiceRegistration {
  type ServiceHandle (line 68) | pub struct ServiceHandle(Rc<ServiceRegistration>);
    method service_id (line 264) | pub fn service_id(&self) -> ServiceId {
  type ResponseEnvelope (line 70) | struct ResponseEnvelope {
  type Item (line 244) | type Item = Bytes;
  type Error (line 245) | type Error = ();
  method poll (line 247) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  method drop (line 270) | fn drop(&mut self) {

FILE: xray_core/src/stream_ext.rs
  type StreamExt (line 7) | pub trait StreamExt
    method wait_next (line 11) | fn wait_next(&mut self, reactor: &mut reactor::Core) -> Option<Self::I...
    method throttle (line 30) | fn throttle<'a>(self, millis: u64) -> Box<'a + Stream<Item = Self::Ite...

FILE: xray_core/src/tree.rs
  constant MIN_CHILDREN (line 6) | const MIN_CHILDREN: usize = 2;
  constant MAX_CHILDREN (line 7) | const MAX_CHILDREN: usize = 4;
  type Item (line 9) | pub trait Item: Clone + Eq + fmt::Debug {
    method summarize (line 12) | fn summarize(&self) -> Self::Summary;
    type Summary (line 664) | type Summary = IntegersSummary;
    method summarize (line 666) | fn summarize(&self) -> Self::Summary {
  type Dimension (line 15) | pub trait Dimension: for<'a> Add<&'a Self, Output = Self> + Ord + Clone ...
    method from_summary (line 18) | fn from_summary(summary: &Self::Summary) -> Self;
    method default (line 20) | fn default() -> Self {
    type Summary (line 682) | type Summary = IntegersSummary;
    method from_summary (line 684) | fn from_summary(summary: &Self::Summary) -> Self {
    type Summary (line 699) | type Summary = IntegersSummary;
    method from_summary (line 701) | fn from_summary(summary: &Self::Summary) -> Self {
  type Tree (line 26) | pub struct Tree<T: Item>(Arc<Node<T>>);
  type Node (line 29) | pub enum Node<T: Item> {
  type Iter (line 42) | pub struct Iter<'a, T: 'a + Item> {
  type Cursor (line 49) | pub struct Cursor<'a, T: 'a + Item> {
  type SeekBias (line 58) | pub enum SeekBias {
  function extend (line 64) | fn extend<I: IntoIterator<Item = T>>(&mut self, items: I) {
  function new (line 72) | pub fn new() -> Self {
  function from_item (line 76) | pub fn from_item(item: T) -> Self {
  function from_children (line 82) | fn from_children(children: Vec<Self>) -> Self {
  function summarize_children (line 97) | fn summarize_children(children: &[Tree<T>]) -> T::Summary {
  function iter (line 105) | pub fn iter(&self) -> Iter<T> {
  function cursor (line 109) | pub fn cursor(&self) -> Cursor<T> {
  function len (line 113) | pub fn len<D: Dimension<Summary = T::Summary>>(&self) -> D {
  function last (line 117) | pub fn last(&self) -> Option<&T> {
  function push (line 121) | pub fn push(&mut self, item: T) {
  function push_tree (line 128) | pub fn push_tree(&mut self, other: Self) {
  function push_recursive (line 150) | fn push_recursive(&mut self, other: Tree<T>) -> Option<Tree<T>> {
  function append_children (line 170) | fn append_children(&mut self, new_children: &[Tree<T>]) -> Option<Tree<T...
  function splice (line 205) | pub fn splice<D: Dimension<Summary = T::Summary>, I: IntoIterator<Item =...
  function append_subsequence (line 217) | fn append_subsequence<D: Dimension<Summary = T::Summary>>(
  function append_subsequence_recursive (line 226) | fn append_subsequence_recursive<D: Dimension<Summary = T::Summary>>(
  function rightmost_leaf (line 258) | fn rightmost_leaf(&self) -> Option<&Tree<T>> {
  function rightmost_leaf_mut (line 267) | fn rightmost_leaf_mut(&mut self) -> &mut Option<Tree<T>> {
  function summary (line 279) | pub fn summary(&self) -> &T::Summary {
  function summary_mut (line 286) | fn summary_mut(&mut self) -> &mut T::Summary {
  function children (line 297) | fn children(&self) -> &[Tree<T>] {
  function last_child_mut (line 304) | fn last_child_mut(&mut self) -> &mut Tree<T> {
  function value (line 313) | fn value(&self) -> &T {
  function underflowing (line 320) | fn underflowing(&self) -> bool {
  function is_empty (line 327) | fn is_empty(&self) -> bool {
  function height (line 334) | fn height(&self) -> u16 {
  function new (line 343) | fn new(tree: &'a Tree<T>) -> Self {
  function seek_to_first_item (line 351) | fn seek_to_first_item(&mut self, mut tree: &'a Tree<T>) -> Option<&'a T> {
  type Item (line 372) | type Item = &'a T;
  method next (line 374) | fn next(&mut self) -> Option<Self::Item> {
  function new (line 397) | fn new(tree: &'tree Tree<T>) -> Self {
  function reset (line 407) | fn reset(&mut self) {
  function start (line 414) | pub fn start<D: Dimension<Summary = T::Summary>>(&self) -> D {
  function item (line 418) | pub fn item<'a>(&'a self) -> Option<&'tree T> {
  function prev_item (line 422) | pub fn prev_item<'a>(&'a self) -> Option<&'tree T> {
  function cur_leaf (line 426) | fn cur_leaf<'a>(&'a self) -> Option<&'tree Tree<T>> {
  function next (line 433) | pub fn next(&mut self) {
  function prev (line 456) | pub fn prev(&mut self) {
  function seek_to_first_item (line 505) | fn seek_to_first_item<'a>(&'a mut self, mut tree: &'tree Tree<T>) {
  function seek_to_last_item (line 521) | fn seek_to_last_item<'a>(&'a mut self, mut tree: &'tree Tree<T>) {
  function seek (line 541) | pub fn seek<D: Dimension<Summary = T::Summary>>(&mut self, pos: &D, bias...
  function slice (line 546) | pub fn slice<D: Dimension<Summary = T::Summary>>(
  function seek_and_slice (line 556) | fn seek_and_slice<D: Dimension<Summary = T::Summary>>(
  type IntegersSummary (line 652) | pub struct IntegersSummary {
    method add_assign (line 675) | fn add_assign(&mut self, other: &Self) {
  type Count (line 658) | struct Count(usize);
    type Output (line 690) | type Output = Self;
    method add (line 692) | fn add(mut self, other: &Self) -> Self {
  type Sum (line 661) | struct Sum(usize);
    type Output (line 707) | type Output = Self;
    method add (line 709) | fn add(mut self, other: &Self) -> Self {
  function items (line 716) | fn items(&self) -> Vec<T> {
  function test_extend_and_push (line 722) | fn test_extend_and_push() {
  function splice (line 735) | fn splice() {
  function random (line 743) | fn random() {
  function cursor (line 795) | fn cursor() {

FILE: xray_core/src/wasm_logging.rs
  function log (line 5) | pub fn log(s: &str);
  function error (line 6) | pub fn error(s: &str);

FILE: xray_core/src/window.rs
  type ViewId (line 12) | pub type ViewId = usize;
  type View (line 14) | pub trait View: Stream<Item = (), Error = ()> {
    method component_name (line 15) | fn component_name(&self) -> &'static str;
    method will_mount (line 16) | fn will_mount(&mut self, &mut Window, WeakViewHandle<Self>)
    method render (line 21) | fn render(&self) -> serde_json::Value;
    method dispatch_action (line 22) | fn dispatch_action(&mut self, serde_json::Value, &mut Window) {}
    method component_name (line 334) | fn component_name(&self) -> &'static str {
    method render (line 338) | fn render(&self) -> serde_json::Value {
    method will_mount (line 342) | fn will_mount(&mut self, window: &mut Window, _view_handle: WeakViewHa...
  type Window (line 25) | pub struct Window(Rc<RefCell<Inner>>);
    method new (line 71) | pub fn new(background: Option<BackgroundExecutor>, height: f64) -> Self {
    method dispatch_action (line 86) | pub fn dispatch_action(&mut self, view_id: ViewId, action: serde_json:...
    method updates (line 91) | pub fn updates(&mut self) -> WindowUpdateStream {
    method set_height (line 101) | pub fn set_height(&mut self, height: f64) {
    method set_root_view (line 105) | pub fn set_root_view(&mut self, root_view: ViewHandle) {
    method height (line 109) | pub fn height(&self) -> f64 {
    method add_view (line 113) | pub fn add_view<T: 'static + View>(&mut self, view: T) -> ViewHandle {
    method spawn (line 136) | pub fn spawn<F: Future<Item = (), Error = ()> + Send + 'static>(&self,...
    method handle (line 144) | pub fn handle(&self) -> WeakWindowHandle {
  type WeakWindowHandle (line 28) | pub struct WeakWindowHandle(Weak<RefCell<Inner>>);
    method map (line 150) | pub fn map<F, T>(&self, f: F) -> Option<T>
  type WindowUpdateStream (line 30) | pub struct WindowUpdateStream {
  type Inner (line 36) | pub struct Inner {
    method notify (line 246) | fn notify(&mut self) {
    method get_view (line 250) | fn get_view(&self, id: ViewId) -> Option<Rc<RefCell<View<Item = (), Er...
  type ViewHandle (line 49) | pub struct ViewHandle {
    method focus (line 256) | pub fn focus(&self) -> Result<(), ()> {
  type WeakViewHandle (line 54) | pub struct WeakViewHandle<T: ?Sized>(Weak<RefCell<T>>);
  type WindowUpdate (line 57) | pub struct WindowUpdate {
  type ViewUpdate (line 64) | pub struct ViewUpdate {
  type Item (line 162) | type Item = WindowUpdate;
  type Error (line 163) | type Error = ();
  method poll (line 165) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  method drop (line 266) | fn drop(&mut self) {
  function map (line 283) | pub fn map<F, R>(&self, f: F) -> Option<R>
  method clone (line 292) | fn clone(&self) -> Self {
  function test_view_handle_drop (line 309) | fn test_view_handle_drop() {
  type TestView (line 315) | struct TestView {
    method new (line 324) | fn new(add_child: bool) -> Self {
  type Item (line 350) | type Item = ();
  type Error (line 351) | type Error = ();
  method poll (line 353) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {

FILE: xray_core/src/workspace.rs
  type Workspace (line 23) | pub trait Workspace {
    method user_id (line 24) | fn user_id(&self) -> UserId;
    method project (line 25) | fn project(&self) -> Ref<Project>;
    method project_mut (line 26) | fn project_mut(&self) -> RefMut<Project>;
    method user_id (line 86) | fn user_id(&self) -> UserId {
    method project (line 90) | fn project(&self) -> Ref<Project> {
    method project_mut (line 94) | fn project_mut(&self) -> RefMut<Project> {
    method user_id (line 114) | fn user_id(&self) -> UserId {
    method project (line 118) | fn project(&self) -> Ref<Project> {
    method project_mut (line 122) | fn project_mut(&self) -> RefMut<Project> {
  type LocalWorkspace (line 29) | pub struct LocalWorkspace {
    method new (line 76) | pub fn new(project: LocalProject) -> Self {
  type RemoteWorkspace (line 35) | pub struct RemoteWorkspace {
    method new (line 100) | pub fn new(
  type WorkspaceService (line 40) | pub struct WorkspaceService {
    method new (line 128) | pub fn new(workspace: Rc<RefCell<LocalWorkspace>>) -> Self {
    type State (line 134) | type State = ServiceState;
    type Update (line 135) | type Update = Never;
    type Request (line 136) | type Request = Never;
    type Response (line 137) | type Response = Never;
    method init (line 139) | fn init(&mut self, connection: &server::Connection) -> ServiceState {
  type ServiceState (line 45) | pub struct ServiceState {
  type WorkspaceView (line 50) | pub struct WorkspaceView {
    method new (line 153) | pub fn new(foreground: ForegroundExecutor, workspace: Rc<RefCell<Works...
    method toggle_file_finder (line 167) | fn toggle_file_finder(&mut self, window: &mut Window) {
    method open_buffer (line 179) | fn open_buffer<T>(&self, buffer: T)
    method save_active_buffer (line 214) | fn save_active_buffer(&self) {
  type Anchor (line 63) | pub struct Anchor {
  type WorkspaceViewAction (line 70) | enum WorkspaceViewAction {
  method component_name (line 233) | fn component_name(&self) -> &'static str {
  method render (line 237) | fn render(&self) -> serde_json::Value {
  method will_mount (line 245) | fn will_mount(&mut self, window: &mut Window, view_handle: WeakViewHandl...
  method dispatch_action (line 250) | fn dispatch_action(&mut self, action: serde_json::Value, window: &mut Wi...
  method set_active_buffer_view (line 260) | fn set_active_buffer_view(&mut self, handle: WeakViewHandle<BufferView>) {
  method search_paths (line 266) | fn search_paths(
  method did_close (line 277) | fn did_close(&mut self) {
  method did_confirm (line 282) | fn did_confirm(&mut self, tree_id: TreeId, path: &cross_platform::Path, ...
  type Item (line 289) | type Item = ();
  type Error (line 290) | type Error = ();
  method poll (line 292) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {

FILE: xray_electron/lib/main_process/main.js
  constant SERVER_PATH (line 7) | const SERVER_PATH = process.env.XRAY_SERVER_PATH;
  constant SOCKET_PATH (line 13) | const SOCKET_PATH = process.env.XRAY_SOCKET_PATH;
  class XrayApplication (line 19) | class XrayApplication {
    method constructor (line 20) | constructor (serverPath, socketPath) {
    method start (line 28) | async start () {
    method _handleMessage (line 48) | async _handleMessage (message) {
    method _createWindow (line 58) | _createWindow (windowId) {

FILE: xray_electron/lib/render_process/main.js
  function start (line 8) | async function start() {

FILE: xray_electron/lib/shared/xray_client.js
  method constructor (line 6) | constructor () {
  method start (line 12) | start (socketPath) {
  method sendMessage (line 26) | sendMessage (message) {
  method addMessageListener (line 31) | addMessageListener (callback) {
  method removeMessageListener (line 35) | removeMessageListener (callback) {
  method _handleInput (line 39) | _handleInput (input) {

FILE: xray_server/src/fs.rs
  type Tree (line 17) | pub struct Tree {
    method new (line 32) | pub fn new<T: Into<PathBuf>>(path: T) -> Result<Self, &'static str> {
    method populate (line 52) | fn populate(
    method root (line 99) | fn root(&self) -> xray_fs::Entry {
    method updates (line 103) | fn updates(&self) -> Box<Stream<Item = (), Error = ()>> {
    method path (line 109) | fn path(&self) -> &cross_platform::Path {
    method populated (line 113) | fn populated(&self) -> Box<Future<Item = (), Error = ()>> {
    method as_tree (line 123) | fn as_tree(&self) -> &xray_fs::Tree {
  type FileProvider (line 24) | pub struct FileProvider;
    method new (line 129) | pub fn new() -> Self {
    method open (line 135) | fn open(
  type File (line 26) | pub struct File {
    method new (line 161) | fn new(file: fs::File) -> Result<File, io::Error> {
    method id (line 170) | fn id(&self) -> xray_fs::FileId {
    method read (line 174) | fn read(&self) -> Box<Future<Item = String, Error = io::Error>> {
    method write_snapshot (line 191) | fn write_snapshot(

FILE: xray_server/src/json_lines_codec.rs
  type JsonLinesCodec (line 8) | pub struct JsonLinesCodec<In, Out> {
  function new (line 18) | pub fn new() -> Self {
  type Item (line 31) | type Item = In;
  type Error (line 32) | type Error = io::Error;
  method decode (line 34) | fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, S...
  type Item (line 50) | type Item = Out;
  type Error (line 51) | type Error = io::Error;
  method encode (line 53) | fn encode(&mut self, msg: Self::Item, buf: &mut BytesMut) -> io::Result<...

FILE: xray_server/src/main.rs
  function main (line 30) | fn main() {

FILE: xray_server/src/messages.rs
  type IncomingMessage (line 8) | pub enum IncomingMessage {
  type OutgoingMessage (line 37) | pub enum OutgoingMessage {

FILE: xray_server/src/server.rs
  type Server (line 19) | pub struct Server {
    method new (line 25) | pub fn new(headless: bool, reactor: reactor::Handle) -> Self {
    method accept_connection (line 35) | pub fn accept_connection<'a, S>(&mut self, socket: S)
    method start_app (line 64) | fn start_app<O, I>(&self, outgoing: O, incoming: I)
    method start_cli (line 99) | fn start_cli<O, I>(&self, outgoing: O, incoming: I, headless: bool)
    method start_window (line 128) | pub fn start_window<O, I>(&self, outgoing: O, incoming: I, window_id: ...
    method handle_app_message (line 160) | fn handle_app_message(
    method handle_window_message (line 182) | fn handle_window_message(&self, window_id: WindowId, message: Incoming...
    method close_window (line 195) | fn close_window(&self, window_id: WindowId) -> Result<(), String> {
    method open_workspace (line 202) | fn open_workspace(&self, paths: Vec<PathBuf>) -> Result<(), String> {
    method tcp_listen (line 215) | fn tcp_listen(&self, port: u16) -> Result<(), String> {
    method connect_to_peer (line 246) | fn connect_to_peer(&self, address: SocketAddr) -> Box<Future<Item = ()...
    method send_outgoing (line 288) | fn send_outgoing<O, I>(&self, outgoing: O, responses: I)
  function report_input_errors (line 301) | fn report_input_errors<S>(incoming: S) -> Box<Stream<Item = OutgoingMess...

FILE: xray_ui/lib/action_dispatcher.js
  class ActionSet (line 9) | class ActionSet {
    method constructor (line 10) | constructor() {
  class ActionDispatcher (line 16) | class ActionDispatcher extends React.Component {
    method constructor (line 17) | constructor() {
    method render (line 24) | render() {
    method handleKeyDown (line 28) | handleKeyDown(event) {
    method getChildContext (line 55) | getChildContext() {
  class ActionContext (line 68) | class ActionContext extends React.Component {
    method constructor (line 69) | constructor() {
    method componentWillMount (line 74) | componentWillMount() {
    method componentDidMount (line 100) | componentDidMount() {
    method render (line 109) | render() {
    method getChildContext (line 113) | getChildContext() {
  class Action (line 129) | class Action extends React.Component {
    method constructor (line 130) | constructor() {
    method render (line 135) | render() {
    method componentDidMount (line 139) | componentDidMount() {
    method dispatch (line 146) | dispatch() {
  function keystrokeStringForEvent (line 156) | function keystrokeStringForEvent(event) {
  function appendKeystrokeElement (line 176) | function appendKeystrokeElement(keyString, element) {
  function contextMatches (line 182) | function contextMatches(context, expression) {

FILE: xray_ui/lib/app.js
  class App (line 90) | class App extends React.Component {
    method constructor (line 91) | constructor(props) {
    method getChildContext (line 95) | getChildContext() {
    method render (line 102) | render() {

FILE: xray_ui/lib/debounce.js
  function later (line 5) | function later () {

FILE: xray_ui/lib/file_finder.js
  class SelectedSearchResultListItem (line 50) | class SelectedSearchResultListItem extends React.Component {
    method render (line 51) | render() {
    method componentDidMount (line 61) | componentDidMount() {
    method componentDidUpdate (line 65) | componentDidUpdate() {
    method scrollIntoViewIfNeeded (line 69) | scrollIntoViewIfNeeded() {
  method constructor (line 76) | constructor() {
  method render (line 82) | render() {
  method renderSearchResult (line 109) | renderSearchResult({ positions, display_path }, isSelected) {
  method focus (line 136) | focus() {
  method didChangeQuery (line 140) | didChangeQuery(event) {
  method didChangeIncludeIgnored (line 147) | didChangeIncludeIgnored(event) {

FILE: xray_ui/lib/modal.js
  method render (line 17) | render() {
  method componentDidMount (line 21) | componentDidMount() {
  method componentWillUnmount (line 25) | componentWillUnmount() {

FILE: xray_ui/lib/text_editor/text_editor.js
  constant CURSOR_BLINK_RESUME_DELAY (line 10) | const CURSOR_BLINK_RESUME_DELAY = 300;
  constant CURSOR_BLINK_PERIOD (line 11) | const CURSOR_BLINK_PERIOD = 800;
  class TextEditor (line 20) | class TextEditor extends React.Component {
    method getDerivedStateFromProps (line 21) | static getDerivedStateFromProps(nextProps, prevState) {
    method constructor (line 39) | constructor(props) {
    method componentDidMount (line 53) | componentDidMount() {
    method componentWillUnmount (line 80) | componentWillUnmount() {
    method componentDidResize (line 89) | componentDidResize(measurements) {
    method render (line 97) | render() {
    method handleMouseDown (line 213) | handleMouseDown(event) {
    method handleClick (line 227) | handleClick({ clientX, clientY }) {
    method handleDoubleClick (line 259) | handleDoubleClick() {
    method handleTripleClick (line 264) | handleTripleClick() {
    method handleMouseWheel (line 269) | handleMouseWheel(event) {
    method handleKeyDown (line 277) | handleKeyDown(event) {
    method pauseCursorBlinking (line 286) | pauseCursorBlinking() {
    method stopCursorBlinking (line 291) | stopCursorBlinking() {
    method startCursorBlinking (line 302) | startCursorBlinking() {
    method focus (line 315) | focus() {
    method flushHorizontalAutoscroll (line 319) | flushHorizontalAutoscroll() {
    method getScrollLeft (line 359) | getScrollLeft() {
    method getScrollRight (line 363) | getScrollRight() {
    method setScrollLeft (line 371) | setScrollLeft(scrollLeft) {
    method constrainScrollLeft (line 377) | constrainScrollLeft(scrollLeft) {
    method getMaxScrollLeft (line 381) | getMaxScrollLeft() {
    method getScrollWidth (line 390) | getScrollWidth() {
    method getContentWidth (line 399) | getContentWidth() {
    method getBaseCharacterWidth (line 414) | getBaseCharacterWidth() {
    method getLongestLineWidth (line 421) | getLongestLineWidth() {
    method getGutterWidth (line 430) | getGutterWidth() {
    method canUseTextPlane (line 441) | canUseTextPlane() {

FILE: xray_ui/lib/text_editor/text_plane.js
  class TextPlane (line 6) | class TextPlane extends React.Component {
    method constructor (line 7) | constructor(props) {
    method render (line 12) | render() {
    method handleCanvas (line 25) | handleCanvas(canvas) {
    method componentDidUpdate (line 29) | async componentDidUpdate() {
    method measureLine (line 78) | measureLine(line, column = line.length) {
    method layoutLine (line 88) | layoutLine(line) {
    method getGutterWidth (line 94) | getGutterWidth(totalRowCount) {
    method isReady (line 100) | isReady() {
  constant UNIT_QUAD_VERTICES (line 112) | const UNIT_QUAD_VERTICES = new Float32Array([1, 1, 1, 0, 0, 0, 0, 1]);
  constant UNIT_QUAD_ELEMENT_INDICES (line 113) | const UNIT_QUAD_ELEMENT_INDICES = new Uint8Array([0, 1, 3, 1, 2, 3]);
  constant MAX_INSTANCES (line 114) | const MAX_INSTANCES = 1 << 16;
  constant GLYPH_INSTANCE_SIZE (line 115) | const GLYPH_INSTANCE_SIZE = 12;
  constant GLYPH_INSTANCE_SIZE_IN_BYTES (line 116) | const GLYPH_INSTANCE_SIZE_IN_BYTES =
  constant SOLID_INSTANCE_SIZE (line 118) | const SOLID_INSTANCE_SIZE = 8;
  constant SOLID_INSTANCE_SIZE_IN_BYTES (line 119) | const SOLID_INSTANCE_SIZE_IN_BYTES =
  constant SUBPIXEL_DIVISOR (line 121) | const SUBPIXEL_DIVISOR = 4;
  class Renderer (line 123) | class Renderer {
    method constructor (line 124) | constructor(gl, style) {
    method createBuffers (line 194) | createBuffers() {
    method createTextBlendVAO (line 232) | createTextBlendVAO() {
    method createSolidVAO (line 312) | createSolidVAO() {
    method layoutLine (line 370) | layoutLine(line) {
    method draw (line 382) | draw({
    method clearRectangle (line 485) | clearRectangle(x, y, width, height, viewportScaleX, viewportScaleY) {
    method drawSelections (line 519) | drawSelections(
    method drawText (line 555) | drawText(
    method drawCursors (line 613) | drawCursors(cursorSolidCount, scrollLeft, viewportScaleX, viewportScal...
    method populateGutterGlyphInstances (line 638) | populateGutterGlyphInstances(
    method populateLineGlyphInstances (line 681) | populateLineGlyphInstances(
    method updateGlyphInstance (line 743) | updateGlyphInstance(glyphInstances, i, x, y, glyph, color) {
    method populateSelectionSolidInstances (line 764) | populateSelectionSolidInstances(
    method updateSolidInstance (line 875) | updateSolidInstance(arrayBuffer, i, x, y, width, height, color) {
    method getGutterWidth (line 890) | getGutterWidth(totalRowCount) {
    method createProgram (line 895) | createProgram(vertexShader, fragmentShader) {
    method createShader (line 907) | createShader(source, type) {
  class Atlas (line 921) | class Atlas {
    method constructor (line 922) | constructor(gl, style) {
    method getGlyph (line 971) | getGlyph(text, variantIndex) {
    method rasterizeGlyph (line 987) | rasterizeGlyph(text, variantIndex) {
    method uploadTexture (line 1023) | uploadTexture() {
  function comparePoints (line 1041) | function comparePoints(a, b) {
  function keyForPoint (line 1045) | function keyForPoint(point) {

FILE: xray_ui/lib/theme_provider.js
  class ThemeProvider (line 4) | class ThemeProvider extends React.Component {
    method render (line 5) | render() {
    method getChildContext (line 9) | getChildContext() {

FILE: xray_ui/lib/view.js
  class View (line 6) | class View extends React.Component {
    method constructor (line 7) | constructor(props) {
    method componentWillReceiveProps (line 16) | componentWillReceiveProps(props, context) {
    method componentDidMount (line 25) | componentDidMount() {
    method componentWillUnmount (line 29) | componentWillUnmount() {
    method render (line 34) | render() {
    method dispatchAction (line 49) | dispatchAction(action) {
    method watch (line 55) | watch(props, context) {
    method getChildContext (line 66) | getChildContext() {

FILE: xray_ui/lib/view_registry.js
  method constructor (line 4) | constructor({ onAction } = {}) {
  method addComponent (line 12) | addComponent(name, component) {
  method getComponent (line 17) | getComponent(id) {
  method removeComponent (line 25) | removeComponent(name) {
  method update (line 29) | update({ updated, removed, focused }) {
  method getProps (line 59) | getProps(id) {
  method watchProps (line 65) | watchProps(id, callback) {
  method watchFocus (line 82) | watchFocus(id, callback) {
  method dispatchAction (line 94) | dispatchAction(id, action) {

FILE: xray_ui/lib/workspace.js
  class Workspace (line 45) | class Workspace extends React.Component {
    method constructor (line 46) | constructor() {
    method render (line 50) | render() {
    method componentDidMount (line 83) | componentDidMount() {

FILE: xray_ui/test/helpers/component_helpers.js
  method shallow (line 7) | shallow(node, options) {
  method mount (line 11) | mount(node, options) {
  method setProps (line 15) | setProps(wrapper, props) {
  function addStyletronToContext (line 20) | function addStyletronToContext(options = {}) {

FILE: xray_ui/test/modal.test.js
  class FocusableComponent (line 58) | class FocusableComponent extends React.Component {
    method render (line 59) | render() {
  function buildAndAttachElement (line 64) | function buildAndAttachElement(tagName) {

FILE: xray_ui/test/view.test.js
  class TestComponent (line 75) | class TestComponent extends React.Component {
    method render (line 76) | render() {
    method focus (line 80) | focus() {

FILE: xray_wasm/lib/support.js
  class JsSink (line 1) | class JsSink {
    method constructor (line 2) | constructor({ send, close }) {
    method send (line 7) | send(message) {
    method close (line 11) | close() {
  function notifyOnNextTick (line 17) | function notifyOnNextTick(notifyHandle) {

FILE: xray_wasm/src/lib.rs
  type OutgoingMessage (line 28) | enum OutgoingMessage {
  type IncomingMessage (line 36) | enum IncomingMessage {
  type Executor (line 44) | pub struct Executor(Rc<RefCell<ExecutorState>>);
    method new (line 95) | fn new() -> Self {
    method execute (line 108) | fn execute(&self, future: F) -> Result<(), future::ExecuteError<F>> {
  type ExecutorState (line 46) | struct ExecutorState {
  type NotifyHandle (line 55) | pub struct NotifyHandle(Weak<RefCell<ExecutorState>>);
    method notify_from_js_on_next_tick (line 138) | pub fn notify_from_js_on_next_tick(&self) {
  type Channel (line 58) | pub struct Channel {
    method new (line 186) | pub fn new() -> Channel {
    method take_sender (line 194) | pub fn take_sender(&mut self) -> Sender {
    method take_receiver (line 198) | pub fn take_receiver(&mut self) -> Receiver {
  type Sender (line 65) | pub struct Sender(Option<mpsc::UnboundedSender<Bytes>>);
    method send (line 205) | pub fn send(&mut self, message: Vec<u8>) -> bool {
    method dispose (line 213) | pub fn dispose(&mut self) {
  type Receiver (line 68) | pub struct Receiver(mpsc::UnboundedReceiver<Bytes>);
  type Server (line 71) | pub struct Server {
    method new (line 251) | pub fn new() -> Self {
    method start_app (line 266) | pub fn start_app(&mut self, outgoing: JsSink) {
    method start_window (line 286) | pub fn start_window(&mut self, window_id: WindowId, incoming: Receiver...
    method connect_to_peer (line 329) | pub fn connect_to_peer(&mut self, incoming: Receiver, outgoing: JsSink) {
  type FileProvider (line 76) | struct FileProvider;
    method open (line 355) | fn open(
  function notify_on_next_tick (line 83) | fn notify_on_next_tick(notify: NotifyHandle);
  function send (line 88) | fn send(this: &JsSink, message: Vec<u8>);
  function close (line 91) | fn close(this: &JsSink);
  method notify (line 163) | fn notify(&self, id: usize) {
  type Item (line 219) | type Item = Bytes;
  type Error (line 220) | type Error = ();
  method poll (line 222) | fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  type SinkItem (line 228) | type SinkItem = Vec<u8>;
  type SinkError (line 229) | type SinkError = ();
  method start_send (line 231) | fn start_send(
  method poll_complete (line 239) | fn poll_complete(&mut self) -> Result<Async<()>, Self::SinkError> {
  method close (line 243) | fn close(&mut self) -> Result<Async<()>, Self::SinkError> {
  type Test (line 365) | pub struct Test {
    method new (line 372) | pub fn new() -> Self {
    method echo_stream (line 378) | pub fn echo_stream(&self, incoming: Receiver, outgoing: JsSink) {

FILE: xray_wasm/test/tests.js
  method send (line 16) | send(message) {
  method close (line 21) | close() {
Condensed preview — 141 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,216K chars).
[
  {
    "path": ".gitignore",
    "chars": 138,
    "preview": "**/node_modules\n**/target/\n**/*.rs.bk\n**/.DS_Store\n**/.cargo\nIcon*\n.tags*\nxray_wasm/dist\nxray_browser/dist\nmemo_js/dist\n"
  },
  {
    "path": ".travis.yml",
    "chars": 506,
    "preview": "language: rust\n\nbefore_install:\n- curl -o- -L https://yarnpkg.com/install.sh | bash\n- export PATH=\"$HOME/.yarn/bin:$PATH"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 4246,
    "preview": "# Contributing to Xray\n\nThis project is still in the very early days, and isn't going to be usable for even basic editin"
  },
  {
    "path": "Cargo.toml",
    "chars": 127,
    "preview": "[workspace]\nmembers = [\n    \"memo_core\",\n    \"memo_js\",\n    \"xray_core\",\n    \"xray_server\",\n    \"xray_cli\",\n    \"xray_wa"
  },
  {
    "path": "LICENSE",
    "chars": 1063,
    "preview": "MIT License\n\nCopyright (c) 2018 GitHub\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
  },
  {
    "path": "README.md",
    "chars": 16404,
    "preview": "**Attention:** GitHub has decided not to move forward with any aspect of this project. We'll archive the repository in c"
  },
  {
    "path": "docs/architecture/001_client_server_protocol.md",
    "chars": 7639,
    "preview": "# Xray's client/server protocol\n\nXray is organized around a client/server architecture, with all the application logic l"
  },
  {
    "path": "docs/architecture/002_shared_workspaces.md",
    "chars": 6677,
    "preview": "# Shared workspaces\n\n## Current features\n\nAn instance of `xray_server` can host one or more shared workspaces, which can"
  },
  {
    "path": "docs/architecture/003_memo_epochs.md",
    "chars": 2753,
    "preview": "The following document describes the sequence of operations that we should perform when the repository HEAD changes, bot"
  },
  {
    "path": "docs/updates/2018_03_05.md",
    "chars": 9466,
    "preview": "# Update for March 5, 2018\n\n## Contributions\n\nWe received some great contributions from [@dirk](https://github.com/dirk)"
  },
  {
    "path": "docs/updates/2018_03_12.md",
    "chars": 8164,
    "preview": "# Update for March 12, 2018\n\n## Contributions\n\nWe got some help from [@apcragg](https://github.com/apcragg), who made [t"
  },
  {
    "path": "docs/updates/2018_03_19.md",
    "chars": 2004,
    "preview": "# Update for March 19, 2018\n\n## Contributions\n\nWe have a couple of PRs pending ([#36](https://github.com/atom/xray/pull/"
  },
  {
    "path": "docs/updates/2018_03_26.md",
    "chars": 9109,
    "preview": "# Update for March 26, 2018\n\n## Contributions\n\n[@matthewwithanm](https://github.com/matthewwithanm) of Facebook's Nuclid"
  },
  {
    "path": "docs/updates/2018_04_02.md",
    "chars": 9994,
    "preview": "# Update for April 2, 2018\n\n## Contributions\n\n[@chgibb](https://github.com/chgibb) helped us get an initial Travis build"
  },
  {
    "path": "docs/updates/2018_04_09.md",
    "chars": 1718,
    "preview": "# Update for April 9, 2018\n\n## Shared workspaces\n\nWe spent the entire week [laying down the foundations that will enable"
  },
  {
    "path": "docs/updates/2018_04_16.md",
    "chars": 2790,
    "preview": "# Update for April 16, 2018\n\n## Contributions\n\n[@rleungx](https://github.com/rleungx) [set up a basic benchmarking frame"
  },
  {
    "path": "docs/updates/2018_04_23.md",
    "chars": 4687,
    "preview": "# Update for April 23, 2018\n\n## An initial implementation of shared workspaces is complete\n\nLast week we [completed the "
  },
  {
    "path": "docs/updates/2018_04_30.md",
    "chars": 2097,
    "preview": "# Update for April 30, 2018\n\n## Xray now runs in a browser\n\nLast week, we merged [#67](https://github.com/atom/xray/pull"
  },
  {
    "path": "docs/updates/2018_05_07.md",
    "chars": 4533,
    "preview": "# Update for May 7, 2018\n\n## Contributions\n\n[@yajamon](https://github.com/yajamon) contributed [a fix for an oversight i"
  },
  {
    "path": "docs/updates/2018_05_14.md",
    "chars": 5576,
    "preview": "# Update for May 14, 2018\n\n## More optimizations\n\nLast week we spent a couple of days speeding up multi-cursor editing. "
  },
  {
    "path": "docs/updates/2018_05_28.md",
    "chars": 5010,
    "preview": "# Update for May 28, 2018\n\n## Staying the course with CRDT-based version control\n\nIn the last update, I said that we wer"
  },
  {
    "path": "docs/updates/2018_07_10.md",
    "chars": 6875,
    "preview": "# Update for July 10, 2018\n\nIt's been a while since the last update, and I apologize for that. Our strategic direction h"
  },
  {
    "path": "docs/updates/2018_07_16.md",
    "chars": 5977,
    "preview": "# Update for July 16, 2018\n\n## Breaking cycles\n\nThis week, we continued our focus on a fully replicated model of the fil"
  },
  {
    "path": "docs/updates/2018_07_23.md",
    "chars": 5593,
    "preview": "# Update for July 23, 2018\n\n## Contributions\n\n[@MoritzKn](https://github.com/MoritzKn) [fixed a bug](https://github.com/"
  },
  {
    "path": "docs/updates/2018_07_31.md",
    "chars": 8681,
    "preview": "# Update for July 31, 2018\n\n## Contributions\n\n[@Aerijo](https://github.com/Aerijo) encountered some confusion and took i"
  },
  {
    "path": "docs/updates/2018_08_21.md",
    "chars": 2805,
    "preview": "# Update for August 21, 2018\n\n## *Eon* is now *Memo*\n\nI chose the name *Eon* fairly hastily and ended up kind of disliki"
  },
  {
    "path": "docs/updates/2018_08_28.md",
    "chars": 2539,
    "preview": "# Update for August 28, 2018\n\n## Convergence for files and hard links\n\nAs predicted in the [last update](./2018_08_21.md"
  },
  {
    "path": "docs/updates/2018_09_14.md",
    "chars": 7230,
    "preview": "# Update for September 14, 2018\n\nIt's been an intense couple of weeks, but we're coming out of it with more clarity than"
  },
  {
    "path": "docs/updates/2018_10_02.md",
    "chars": 5857,
    "preview": "# Update for October 2, 2018\n\n## Shipped an initial light client\n\nLast week, we [shipped](https://github.com/atom/xray/p"
  },
  {
    "path": "memo_core/Cargo.toml",
    "chars": 454,
    "preview": "[package]\nname = \"memo_core\"\nversion = \"0.1.0\"\nauthors = [\"Antonio Scandurra <as-cii@github.com>\", \"Nathan Sobo <nathan@"
  },
  {
    "path": "memo_core/README.md",
    "chars": 4809,
    "preview": "# Memo – Real-time collaboration for Git\n\n**This project is a work in progress. This README defines the vision, but it i"
  },
  {
    "path": "memo_core/rustfmt.toml",
    "chars": 17,
    "preview": "edition = \"2018\"\n"
  },
  {
    "path": "memo_core/script/compile_flatbuffers",
    "chars": 208,
    "preview": "#!/bin/bash\n\nflatc --rust -o src/serialization src/serialization/schema.fbs\n\n# Workaround for incorrect code generation "
  },
  {
    "path": "memo_core/src/btree.rs",
    "chars": 47735,
    "preview": "use smallvec::SmallVec;\nuse std::cmp::Ordering;\nuse std::fmt;\nuse std::ops::{Add, AddAssign};\nuse std::sync::Arc;\n\n#[cfg"
  },
  {
    "path": "memo_core/src/buffer.rs",
    "chars": 112286,
    "preview": "use crate::btree::{self, SeekBias};\nuse crate::operation_queue::{self, OperationQueue};\nuse crate::serialization;\nuse cr"
  },
  {
    "path": "memo_core/src/epoch.rs",
    "chars": 93348,
    "preview": "use crate::btree::{self, SeekBias};\nuse crate::buffer::{self, Buffer, Point, Selection, SelectionSetId, Text};\nuse crate"
  },
  {
    "path": "memo_core/src/lib.rs",
    "chars": 6781,
    "preview": "mod btree;\nmod buffer;\nmod epoch;\n#[allow(non_snake_case, unused_imports)]\nmod operation_queue;\nmod serialization;\npub m"
  },
  {
    "path": "memo_core/src/operation_queue.rs",
    "chars": 3443,
    "preview": "use crate::btree::{Cursor, Dimension, Edit, Item, KeyedItem, Tree};\nuse crate::time;\nuse std::fmt::Debug;\nuse std::ops::"
  },
  {
    "path": "memo_core/src/serialization/mod.rs",
    "chars": 58,
    "preview": "mod schema_generated;\n\npub use self::schema_generated::*;\n"
  },
  {
    "path": "memo_core/src/serialization/schema.fbs",
    "chars": 1985,
    "preview": "struct ReplicaId {\n  first_8_bytes: uint64;\n  last_8_bytes: uint64;\n}\n\nstruct Timestamp {\n  value:uint64;\n  replica_id:R"
  },
  {
    "path": "memo_core/src/serialization/schema_generated.rs",
    "chars": 75842,
    "preview": "// automatically generated by the FlatBuffers compiler, do not modify\n\n\n#![allow(dead_code)]\n#![allow(unused_imports)]\ne"
  },
  {
    "path": "memo_core/src/time.rs",
    "chars": 6251,
    "preview": "use crate::serialization;\nuse crate::Error;\nuse crate::ReplicaId;\nuse crate::ReplicaIdExt;\nuse flatbuffers::{FlatBufferB"
  },
  {
    "path": "memo_core/src/work_tree.rs",
    "chars": 72916,
    "preview": "use crate::buffer::{self, Change, Point, Text};\nuse crate::epoch::{self, Cursor, DirEntry, Epoch, FileId, FileType};\nuse"
  },
  {
    "path": "memo_js/.npmignore",
    "chars": 71,
    "preview": "script\ntest\nCargo.toml\nREADME.md\nnode_modules\ntarget\nwebpack.config.js\n"
  },
  {
    "path": "memo_js/.nvmrc",
    "chars": 7,
    "preview": "11.9.0\n"
  },
  {
    "path": "memo_js/Cargo.toml",
    "chars": 469,
    "preview": "[package]\nname = \"memo_js\"\nversion = \"0.1.0\"\nauthors = [\"Antonio Scandurra <as-cii@github.com>\", \"Nathan Sobo <nathan@gi"
  },
  {
    "path": "memo_js/README.md",
    "chars": 11146,
    "preview": "# Memo JS\n\nMemo allows multiple remote collaborators to share the state of a single Git working copy. The core of the li"
  },
  {
    "path": "memo_js/package.json",
    "chars": 743,
    "preview": "{\n  \"name\": \"@atom/memo\",\n  \"version\": \"0.21.0\",\n  \"main\": \"dist/index.node.js\",\n  \"browser\": \"dist/index.js\",\n  \"types\""
  },
  {
    "path": "memo_js/rustfmt.toml",
    "chars": 17,
    "preview": "edition = \"2018\"\n"
  },
  {
    "path": "memo_js/script/build",
    "chars": 760,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\nLOCAL_CRATE_PATH=./.cargo\nPATH=$LOCAL_CRATE_PATH/bin:$PATH\nWASM_BINDGEN_VERSION=0.2.33\n\nset"
  },
  {
    "path": "memo_js/src/index.ts",
    "chars": 5418,
    "preview": "export {\n  BaseEntry,\n  Change,\n  GitProvider,\n  FileType,\n  Oid,\n  Path,\n  Point,\n  Range,\n  ReplicaId,\n  SelectionRang"
  },
  {
    "path": "memo_js/src/lib.rs",
    "chars": 21568,
    "preview": "#![feature(macros_in_extern)]\n\nuse bincode;\nuse futures::{Async, Future, Poll, Stream};\nuse memo_core as memo;\nuse serde"
  },
  {
    "path": "memo_js/src/support.ts",
    "chars": 4025,
    "preview": "export type Tagged<BaseType, TagName> = BaseType & { __tag: TagName };\nexport type Oid = string;\nexport type Path = stri"
  },
  {
    "path": "memo_js/test/tests.ts",
    "chars": 22187,
    "preview": "import {\n  BaseEntry as MemoBaseEntry,\n  Change,\n  GitProvider,\n  FileStatus,\n  FileType,\n  Oid,\n  Operation,\n  Operatio"
  },
  {
    "path": "memo_js/test/tsconfig.json",
    "chars": 254,
    "preview": "{\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/\",\n    \"noImplicitAny\": true,\n    \"strictNullChecks\": true,\n    \"module\":"
  },
  {
    "path": "memo_js/tsconfig.json",
    "chars": 275,
    "preview": "{\n  \"include\": [\"src/**/*.ts\"],\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/\",\n    \"noImplicitAny\": true,\n    \"strictNu"
  },
  {
    "path": "memo_js/webpack.config.js",
    "chars": 848,
    "preview": "var path = require(\"path\");\n\nconst baseConfig = {\n  target: \"node\",\n  module: {\n    rules: [\n      {\n        test: /\\.ts"
  },
  {
    "path": "rust-toolchain",
    "chars": 19,
    "preview": "nightly-2019-01-26\n"
  },
  {
    "path": "script/bench",
    "chars": 51,
    "preview": "#!/bin/bash\n\nset -e\n\ncd xray_core\ncargo bench \"$@\"\n"
  },
  {
    "path": "script/build",
    "chars": 261,
    "preview": "#!/bin/bash\n\nset -e\n\ncd xray_electron; yarn install --check-files; cd -\ncd xray_ui; yarn install; cd -\ncd xray_cli; carg"
  },
  {
    "path": "script/cibuild",
    "chars": 59,
    "preview": "#!/bin/bash\n\nset -e\n\nscript/build\nscript/test\nscript/bench\n"
  },
  {
    "path": "script/test",
    "chars": 171,
    "preview": "#!/bin/bash\n\nset -e\n\ncd xray_core; cargo test; cd -\ncd xray_wasm; script/test; cd -\ncd xray_ui; yarn test; cd -\ncd memo_"
  },
  {
    "path": "xray_browser/README.md",
    "chars": 897,
    "preview": "# Xray Browser\n\nThis directory packages Xray for use in a web browser. Because browsers don't provide access to the unde"
  },
  {
    "path": "xray_browser/package.json",
    "chars": 303,
    "preview": "{\n  \"name\": \"xray_browser\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"xray_wasm\": \"../xray_wasm\",\n    \"xray_ui\": \"../"
  },
  {
    "path": "xray_browser/script/build",
    "chars": 357,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\nrm -rf dist\nmkdir -p dist\ncd ../xray_wasm && script/build && cd -\nyarn install --check-file"
  },
  {
    "path": "xray_browser/script/server",
    "chars": 2825,
    "preview": "#!/usr/bin/env node\n\nconst assert = require(\"assert\");\nconst http = require(\"http\");\nconst express = require(\"express\");"
  },
  {
    "path": "xray_browser/src/client.js",
    "chars": 282,
    "preview": "export default class XrayClient {\n  constructor(worker) {\n    this.worker = worker;\n  }\n\n  onMessage(callback) {\n    thi"
  },
  {
    "path": "xray_browser/src/ui.js",
    "chars": 838,
    "preview": "import { React, ReactDOM, App, buildViewRegistry } from \"xray_ui\";\nimport XrayClient from \"./client\";\nconst $ = React.cr"
  },
  {
    "path": "xray_browser/src/worker.js",
    "chars": 2023,
    "preview": "import { xray as xrayPromise, JsSink } from \"xray_wasm\";\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDec"
  },
  {
    "path": "xray_browser/static/index.html",
    "chars": 4593,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\">\n    <style>\n      html, body, #app {\n        width: 100%;\n   "
  },
  {
    "path": "xray_cli/Cargo.toml",
    "chars": 177,
    "preview": "[package]\nname = \"xray_cli\"\nversion = \"0.1.0\"\nauthors = [\"Nathan Sobo <nathan@github.com>\"]\n\n[dependencies]\ndocopt = \"0."
  },
  {
    "path": "xray_cli/README.md",
    "chars": 233,
    "preview": "# Xray CLI\n\nThis crate is an executable that provides a command-line interface for Xray. It can spawn `xray_server` in h"
  },
  {
    "path": "xray_cli/src/main.rs",
    "chars": 5959,
    "preview": "extern crate docopt;\n#[macro_use]\nextern crate serde_derive;\nextern crate serde_json;\n\nuse docopt::Docopt;\nuse std::env;"
  },
  {
    "path": "xray_core/Cargo.toml",
    "chars": 565,
    "preview": "[package]\nname = \"xray_core\"\nversion = \"0.1.0\"\nauthors = [\"Nathan Sobo <nathan@github.com>\"]\nlicense = \"MIT\"\n\n[dependenc"
  },
  {
    "path": "xray_core/README.md",
    "chars": 335,
    "preview": "# Xray Core\n\nThis directory contains the native core of application as a pure Rust library that is agnostic to the detai"
  },
  {
    "path": "xray_core/benches/bench.rs",
    "chars": 2032,
    "preview": "extern crate xray_core;\n#[macro_use]\nextern crate criterion;\n\nuse criterion::Criterion;\nuse std::cell::RefCell;\nuse std:"
  },
  {
    "path": "xray_core/src/app.rs",
    "chars": 17597,
    "preview": "use bytes::Bytes;\nuse fs;\nuse futures::unsync::mpsc::{self, UnboundedReceiver, UnboundedSender};\nuse futures::{future, A"
  },
  {
    "path": "xray_core/src/buffer.rs",
    "chars": 117405,
    "preview": "use super::rpc::{client, Error as RpcError};\nuse super::tree::{self, SeekBias, Tree};\nuse fs;\nuse futures::{unsync, Futu"
  },
  {
    "path": "xray_core/src/buffer_view.rs",
    "chars": 74917,
    "preview": "use buffer::{self, Buffer, BufferId, Point, Selection, SelectionSetId};\nuse futures::{Future, Poll, Stream};\nuse movemen"
  },
  {
    "path": "xray_core/src/cross_platform.rs",
    "chars": 3598,
    "preview": "use std::borrow::Cow;\n#[cfg(unix)]\nuse std::ffi::{OsStr, OsString};\n#[cfg(unix)]\nuse std::path::PathBuf;\n\npub const UNIX"
  },
  {
    "path": "xray_core/src/file_finder.rs",
    "chars": 5122,
    "preview": "use cross_platform;\nuse futures::{Async, Poll, Stream};\nuse notify_cell::{NotifyCell, NotifyCellObserver};\nuse project::"
  },
  {
    "path": "xray_core/src/fs.rs",
    "chars": 17153,
    "preview": "use buffer::BufferSnapshot;\nuse cross_platform;\nuse futures::{Async, Future, Stream};\nuse notify_cell::NotifyCell;\nuse p"
  },
  {
    "path": "xray_core/src/fuzzy.rs",
    "chars": 9432,
    "preview": "use std::f64;\nuse std::fmt;\nuse std::ops::{Index, IndexMut};\n\npub type Score = f64;\n\npub const SCORE_MIN: Score = f64::N"
  },
  {
    "path": "xray_core/src/lib.rs",
    "chars": 1361,
    "preview": "#![feature(unsize, coerce_unsized)]\n\nextern crate bincode;\nextern crate bytes;\n#[macro_use]\nextern crate lazy_static;\nex"
  },
  {
    "path": "xray_core/src/movement.rs",
    "chars": 2732,
    "preview": "use buffer::{Buffer, Point};\nuse std::char::decode_utf16;\nuse std::cmp;\n\npub fn left(buffer: &Buffer, mut point: Point) "
  },
  {
    "path": "xray_core/src/never.rs",
    "chars": 59,
    "preview": "#[derive(Debug, Serialize, Deserialize)]\npub enum Never {}\n"
  },
  {
    "path": "xray_core/src/notify_cell.rs",
    "chars": 6661,
    "preview": "use std::sync::{Arc, Weak};\nuse futures::{Async, Poll, Stream};\nuse futures::task::{self, Task};\nuse parking_lot::RwLock"
  },
  {
    "path": "xray_core/src/project.rs",
    "chars": 32266,
    "preview": "use buffer::{self, Buffer, BufferId};\nuse cross_platform;\nuse fs;\nuse futures::{future, Async, Future, Poll};\nuse fuzzy;"
  },
  {
    "path": "xray_core/src/rpc/client.rs",
    "chars": 13065,
    "preview": "use super::messages::{MessageToClient, MessageToServer, RequestId, Response, ServiceId};\nuse super::{server, Error};\nuse"
  },
  {
    "path": "xray_core/src/rpc/messages.rs",
    "chars": 672,
    "preview": "use super::Error;\nuse bytes::Bytes;\nuse std::collections::{HashMap, HashSet};\n\npub type RequestId = usize;\npub type Serv"
  },
  {
    "path": "xray_core/src/rpc/mod.rs",
    "chars": 14817,
    "preview": "pub mod client;\nmod messages;\npub mod server;\n\npub use self::messages::{Response, ServiceId};\nuse std::error;\nuse std::f"
  },
  {
    "path": "xray_core/src/rpc/server.rs",
    "chars": 10680,
    "preview": "use super::messages::{MessageToClient, MessageToServer, RequestId, Response, ServiceId};\nuse super::Error;\nuse bincode::"
  },
  {
    "path": "xray_core/src/stream_ext.rs",
    "chars": 1082,
    "preview": "use futures::{Future, Poll, Stream};\nuse std::fmt::Debug;\nuse std::time;\nuse tokio_core::reactor;\nuse tokio_timer::Inter"
  },
  {
    "path": "xray_core/src/tree.rs",
    "chars": 31290,
    "preview": "use std::clone::Clone;\nuse std::fmt;\nuse std::ops::{Add, AddAssign, Range};\nuse std::sync::Arc;\n\nconst MIN_CHILDREN: usi"
  },
  {
    "path": "xray_core/src/wasm_logging.rs",
    "chars": 415,
    "preview": "use wasm_bindgen::prelude::*;\n\n#[wasm_bindgen(js_namespace = console)]\nextern \"C\" {\n    pub fn log(s: &str);\n    pub fn "
  },
  {
    "path": "xray_core/src/window.rs",
    "chars": 10061,
    "preview": "use futures::task::{self, Task};\nuse futures::{Async, Future, Poll, Stream};\nuse serde_json;\nuse std::boxed::Box;\nuse st"
  },
  {
    "path": "xray_core/src/workspace.rs",
    "chars": 8938,
    "preview": "use buffer::{self, Buffer, BufferId};\nuse buffer_view::{BufferView, BufferViewDelegate};\nuse cross_platform;\nuse file_fi"
  },
  {
    "path": "xray_electron/.gitignore",
    "chars": 17,
    "preview": "node_modules\nout\n"
  },
  {
    "path": "xray_electron/README.md",
    "chars": 649,
    "preview": "# Xray Electron Shell\n\nThis is the front-end of the desktop application. It spawns an instance of `xray_server`, where t"
  },
  {
    "path": "xray_electron/index.html",
    "chars": 370,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\">\n    <style>\n      html, body, #app {\n        width: 100%;\n   "
  },
  {
    "path": "xray_electron/lib/main_process/main.js",
    "chars": 2559,
    "preview": "const {app, BrowserWindow} = require('electron');\nconst {spawn} = require('child_process');\nconst path = require('path')"
  },
  {
    "path": "xray_electron/lib/render_process/main.js",
    "chars": 1127,
    "preview": "process.env.NODE_ENV = \"production\";\n\nconst { React, ReactDOM, App, buildViewRegistry } = require(\"xray_ui\");\nconst Xray"
  },
  {
    "path": "xray_electron/lib/shared/xray_client.js",
    "chars": 1455,
    "preview": "const net = require(\"net\");\nconst EventEmitter = require('events');\n\nmodule.exports =\nclass XrayClient {\n  constructor ("
  },
  {
    "path": "xray_electron/package.json",
    "chars": 412,
    "preview": "{\n  \"name\": \"Xray\",\n  \"version\": \"0.0.0\",\n  \"main\": \"./lib/main_process/main.js\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n   "
  },
  {
    "path": "xray_server/Cargo.toml",
    "chars": 445,
    "preview": "[package]\nname = \"xray_server\"\nversion = \"0.1.0\"\nauthors = [\"Nathan Sobo <nathan@github.com>\"]\n\n[dependencies]\nbytes = \""
  },
  {
    "path": "xray_server/README.md",
    "chars": 306,
    "preview": "# Xray Server\n\nThis crate is an executable that runs as a server process. It can be run in a headless mode in order to h"
  },
  {
    "path": "xray_server/src/fs.rs",
    "chars": 6774,
    "preview": "use futures::{self, Future, Stream};\nuse ignore::WalkBuilder;\nuse parking_lot::Mutex;\nuse std::char::decode_utf16;\nuse s"
  },
  {
    "path": "xray_server/src/json_lines_codec.rs",
    "chars": 1409,
    "preview": "use std::io;\nuse bytes::BytesMut;\nuse serde::{Deserialize, Serialize};\nuse serde_json;\nuse tokio_io::codec::{Decoder, En"
  },
  {
    "path": "xray_server/src/main.rs",
    "chars": 1449,
    "preview": "mod messages;\nmod server;\nmod fs;\nmod json_lines_codec;\n\nextern crate bytes;\nextern crate futures;\nextern crate futures_"
  },
  {
    "path": "xray_server/src/messages.rs",
    "chars": 817,
    "preview": "use serde_json;\nuse std::net::SocketAddr;\nuse std::path::PathBuf;\nuse xray_core::{ViewId, WindowId, WindowUpdate};\n\n#[de"
  },
  {
    "path": "xray_server/src/server.rs",
    "chars": 12132,
    "preview": "use bytes::Bytes;\nuse fs;\nuse futures::{future, stream, Future, IntoFuture, Sink, Stream};\nuse futures_cpupool::CpuPool;"
  },
  {
    "path": "xray_ui/README.md",
    "chars": 311,
    "preview": "# Xray UI\n\nThis folder houses Xray's user interface, which is implemented in JavaScript and designed to run in a web env"
  },
  {
    "path": "xray_ui/lib/action_dispatcher.js",
    "chars": 5056,
    "preview": "const React = require(\"react\");\nconst ReactDOM = require(\"react-dom\");\nconst propTypes = require(\"prop-types\");\nconst { "
  },
  {
    "path": "xray_ui/lib/app.js",
    "chars": 3950,
    "preview": "const propTypes = require(\"prop-types\");\nconst React = require(\"react\");\nconst { Client: StyletronClient } = require(\"st"
  },
  {
    "path": "xray_ui/lib/debounce.js",
    "chars": 383,
    "preview": "module.exports =\nfunction debounce (fn, wait) {\n  let timestamp, timeout\n\n  function later () {\n    const last = Date.no"
  },
  {
    "path": "xray_ui/lib/file_finder.js",
    "chars": 3617,
    "preview": "const React = require(\"react\");\nconst ReactDOM = require(\"react-dom\");\nconst { styled } = require(\"styletron-react\");\nco"
  },
  {
    "path": "xray_ui/lib/index.js",
    "chars": 708,
    "preview": "const FileFinder = require(\"./file_finder\");\nconst ViewRegistry = require(\"./view_registry\");\nconst Workspace = require("
  },
  {
    "path": "xray_ui/lib/modal.js",
    "chars": 750,
    "preview": "const React = require(\"react\");\nconst ReactDOM = require(\"react-dom\");\nconst { styled } = require(\"styletron-react\");\nco"
  },
  {
    "path": "xray_ui/lib/text_editor/shaders.js",
    "chars": 3140,
    "preview": "exports.textBlendAttributes = {\n  unitQuadVertex: 0,\n  targetOrigin: 1,\n  targetSize: 2,\n  textColorRGBA: 3,\n  atlasOrig"
  },
  {
    "path": "xray_ui/lib/text_editor/text_editor.js",
    "chars": 12559,
    "preview": "const React = require(\"react\");\nconst ReactDOM = require(\"react-dom\");\nconst PropTypes = require(\"prop-types\");\nconst { "
  },
  {
    "path": "xray_ui/lib/text_editor/text_plane.js",
    "chars": 29104,
    "preview": "const React = require(\"react\");\nconst PropTypes = require(\"prop-types\");\nconst { styled } = require(\"styletron-react\");\n"
  },
  {
    "path": "xray_ui/lib/theme_provider.js",
    "chars": 346,
    "preview": "const React = require(\"react\");\nconst PropTypes = require(\"prop-types\");\n\nclass ThemeProvider extends React.Component {\n"
  },
  {
    "path": "xray_ui/lib/view.js",
    "chars": 2008,
    "preview": "const propTypes = require(\"prop-types\");\nconst React = require(\"react\");\nconst $ = React.createElement;\nconst ViewRegist"
  },
  {
    "path": "xray_ui/lib/view_registry.js",
    "chars": 2419,
    "preview": "const assert = require(\"assert\");\n\nmodule.exports = class ViewRegistry {\n  constructor({ onAction } = {}) {\n    this.onA"
  },
  {
    "path": "xray_ui/lib/workspace.js",
    "chars": 1928,
    "preview": "const propTypes = require(\"prop-types\");\nconst React = require(\"react\");\nconst ReactDOM = require(\"react-dom\");\nconst { "
  },
  {
    "path": "xray_ui/package.json",
    "chars": 754,
    "preview": "{\n  \"name\": \"xray_ui\",\n  \"version\": \"0.0.0\",\n  \"main\": \"lib/index.js\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"test\": \"e"
  },
  {
    "path": "xray_ui/test/action_dispatcher.test.js",
    "chars": 4587,
    "preview": "const assert = require(\"assert\");\nconst propTypes = require(\"prop-types\");\nconst React = require(\"react\");\nconst { mount"
  },
  {
    "path": "xray_ui/test/file_finder.test.js",
    "chars": 814,
    "preview": "const assert = require(\"assert\");\nconst {mount, setProps} = require(\"./helpers/component_helpers\");\nconst FileFinder = r"
  },
  {
    "path": "xray_ui/test/helpers/component_helpers.js",
    "chars": 733,
    "preview": "const enzyme = require(\"enzyme\");\nconst Adapter = require(\"enzyme-adapter-react-16\");\n\nenzyme.configure({ adapter: new A"
  },
  {
    "path": "xray_ui/test/modal.test.js",
    "chars": 2154,
    "preview": "const assert = require(\"assert\");\nconst { mount } = require(\"./helpers/component_helpers\");\nconst React = require(\"react"
  },
  {
    "path": "xray_ui/test/view.test.js",
    "chars": 3051,
    "preview": "const assert = require(\"assert\");\nconst React = require(\"react\");\nconst $ = require(\"react\").createElement;\nconst { moun"
  },
  {
    "path": "xray_ui/test/view_registry.test.js",
    "chars": 4348,
    "preview": "const assert = require(\"assert\");\nconst ViewRegistry = require(\"../lib/view_registry\");\n\nsuite(\"ViewRegistry\", () => {\n "
  },
  {
    "path": "xray_wasm/.gitignore",
    "chars": 7,
    "preview": ".cargo\n"
  },
  {
    "path": "xray_wasm/Cargo.toml",
    "chars": 315,
    "preview": "[package]\nname = \"xray_wasm\"\nversion = \"0.1.0\"\nauthors = [\"Antonio Scandurra <as-cii@github.com>\"]\n\n[lib]\ncrate-type = ["
  },
  {
    "path": "xray_wasm/lib/main.js",
    "chars": 83,
    "preview": "export let xray = import(\"../dist/xray_wasm\");\nexport { JsSink } from \"./support\";\n"
  },
  {
    "path": "xray_wasm/lib/support.js",
    "chars": 400,
    "preview": "export class JsSink {\n  constructor({ send, close }) {\n    if (send) this._send = send;\n    if (close) this._close = clo"
  },
  {
    "path": "xray_wasm/package.json",
    "chars": 435,
    "preview": "{\n  \"name\": \"xray_wasm\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Xray server packaged for use in JavaScript\",\n  \"main\": "
  },
  {
    "path": "xray_wasm/script/build",
    "chars": 702,
    "preview": "#!/usr/bin/env bash\n\nLOCAL_CRATE_PATH=./.cargo\nPATH=$LOCAL_CRATE_PATH/bin:$PATH\nWASM_BINDGEN_VERSION=0.2.33\n\nsetup_wasm_"
  },
  {
    "path": "xray_wasm/script/test",
    "chars": 466,
    "preview": "#!/usr/bin/env bash\n\nLOCAL_CRATE_PATH=./.cargo\nPATH=$LOCAL_CRATE_PATH/bin:$PATH\n\nset -e\n\nrm -rf dist\nmkdir -p dist\ncargo"
  },
  {
    "path": "xray_wasm/src/lib.rs",
    "chars": 11378,
    "preview": "extern crate bytes;\nextern crate futures;\nextern crate wasm_bindgen;\n#[macro_use]\nextern crate xray_core;\nextern crate s"
  },
  {
    "path": "xray_wasm/test/tests.js",
    "chars": 860,
    "preview": "import assert from \"assert\";\nimport { xray as xrayPromise, JsSink } from \"../lib/main\";\n\nsuite(\"Server\", () => {\n  let x"
  }
]

About this extraction

This page contains the full source code of the atom-archive/xray GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 141 files (1.1 MB), approximately 268.5k tokens, and a symbol index with 2280 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!