Documentation
¶
Overview ¶
Package coedit is a PDF several people are editing at once.
What is shared is not the file. A file is a settled thing — merging two people's copies of one means merging two piles of bytes, which cannot be done. What is shared is the plan: which pages, from which files, in which order, turned and cropped how, with which bookmarks over them. That is a list, a tree and a handful of fields, and a list, a tree and a handful of fields are things a conflict-free replicated data type merges without asking anybody.
So two people reordering the same document keep both reorderings, two people rotating different pages keep both rotations, and two people rotating the same page settle on one of the two — which is what rotating a page twice means. Nobody waits for a lock and nobody loses work, however many of them there are.
A plan becomes a file through Plan.Build, which hands back an ops.Doc — the same document the command-line tool and the browser build, so what comes out of a shared edit is what comes out of any other.
Index ¶
- Variables
- type Bookmark
- type BookmarkID
- type Files
- type Local
- type Page
- type PageID
- type Plan
- func (p *Plan) AddBookmark(parent, after BookmarkID, title string, page PageID) (BookmarkID, error)
- func (p *Plan) Append(file string, number int) (PageID, error)
- func (p *Plan) Build() (*ops.Doc, error)
- func (p *Plan) Bytes() ([]byte, error)
- func (p *Plan) Crop(page PageID, box []float64) error
- func (p *Plan) Files() Files
- func (p *Plan) InsertAfter(after PageID, file string, number int) (PageID, error)
- func (p *Plan) Len() int
- func (p *Plan) Move(page, after PageID) error
- func (p *Plan) MoveBookmark(node, parent, after BookmarkID) error
- func (p *Plan) Outline() []Bookmark
- func (p *Plan) Pages() []Page
- func (p *Plan) Remove(page PageID) error
- func (p *Plan) RemoveBookmark(node BookmarkID) error
- func (p *Plan) Rotate(page PageID, degrees int) error
- type Store
Constants ¶
This section is empty.
Variables ¶
var AtRoot = structured.TreeRoot
AtRoot is the top of the bookmark tree.
var AtStart = structured.SeqStart
AtStart is where a page goes when it goes first.
var ErrNoSuchFile = errors.New("coedit: no such file")
ErrNoSuchFile reports a page naming a file the plan does not carry.
var ErrNoSuchPage = errors.New("coedit: no such page")
ErrNoSuchPage reports an operation naming a page this replica does not hold — one never added, or one somebody else removed.
Functions ¶
This section is empty.
Types ¶
type Bookmark ¶
type Bookmark struct {
ID BookmarkID
Title string
// Page is the page it points at, by identity rather than by number, so
// that a bookmark still points at its page after somebody else has moved
// it. It is empty for a bookmark that points at nothing.
Page PageID
Children []Bookmark
}
A Bookmark is one entry of the plan's outline.
type Files ¶
type Files interface {
// Put stores a file under a name, replacing whatever was there.
Put(name string, data []byte) error
// Get reads one back.
Get(name string) ([]byte, bool)
// Names is every file the plan carries.
Names() []string
// Remove drops one. The pages that came from it are left as they are and
// will not build until it is put back, which is what removing a file a
// document is made of means.
Remove(name string) error
}
A Files is where the plan keeps the documents its pages come from.
A file is put in once and referred to by name from then on, so a plan that takes three pages out of a hundred-page report carries the report once. The bytes are cut into pieces on their content rather than every so many bytes, which is what makes replacing one page of a file send one piece rather than the file.
type Local ¶
type Local struct {
// contains filtered or unexported fields
}
Local is a plan held on its own, with nobody else editing it: the shape a test takes, and what a participant edits between one connection and the next.
Its snapshot is what a session is joined with, so work done alone is not work lost.
func (*Local) Composite ¶
Composite is the replica underneath, for a caller merging two plans by hand.
func (*Local) Edit ¶
Edit runs fn against the named map part. There is nobody to publish to, so the operations it produced are already everywhere they are going.
func (*Local) Merge ¶
Merge takes everything another replica has that this one lacks. It is what two people who have been editing apart do when they meet, and it is the same merge a session does — there is only one.
type PageID ¶
type PageID = structured.ItemID
A PageID names one page of the plan. It survives being moved, so two people moving the same page mean the same page.
type Plan ¶
type Plan struct {
// contains filtered or unexported fields
}
A Plan is a PDF being edited: the pages in their order, the bookmarks over them, and the files they come from.
func (*Plan) AddBookmark ¶
func (p *Plan) AddBookmark(parent, after BookmarkID, title string, page PageID) (BookmarkID, error)
AddBookmark puts an entry under a parent, after a sibling — AtRoot for the top and the zero identity for first.
func (*Plan) Build ¶
Build turns the plan into a document ready to be written.
Every page named by the plan is taken from the file it names, in the order the plan puts them, turned and cropped as the plan says. The bookmarks are written over the pages they point at; one pointing at a page that is no longer there is left out rather than pointed at the wrong one.
A plan naming a file this replica has not received yet cannot be built, and says which file it is waiting for. That is not a failure — it is what a participant sees for the moment between somebody adding a file and its pieces arriving.
func (*Plan) Crop ¶
Crop sets what of a page is visible, as the four numbers a PDF rectangle is. A box of nothing puts back whatever the source page said.
func (*Plan) InsertAfter ¶
InsertAfter puts a page of a file after another page, or first for AtStart.
func (*Plan) Move ¶
Move puts a page after another one, or first for AtStart. Two people moving the same page settle on one of the two moves, and two people moving different pages keep both.
func (*Plan) MoveBookmark ¶
func (p *Plan) MoveBookmark(node, parent, after BookmarkID) error
MoveBookmark puts an entry under another parent, after another sibling.
func (*Plan) Pages ¶
Pages is every page of the plan, in order.
An item that names no file is not a page. That is not a nicety: one person removing a page while another turns it leaves the item behind with the turn on it and nothing else, because a field written after a removal puts the record back. What it does not put back is which file the page came from, so that is what says whether there is a page there at all.
func (*Plan) RemoveBookmark ¶
func (p *Plan) RemoveBookmark(node BookmarkID) error
RemoveBookmark drops an entry and everything under it.
type Store ¶
type Store interface {
// Read runs fn against the named map part.
Read(part string, fn func(*crdt.Map)) error
// Edit runs fn against the named map part and publishes what it produced.
Edit(part string, fn func(*crdt.Map) ([]crdt.MapOp, error)) error
}
A Store is where a plan keeps its parts: a composite held on its own, or one shared with everybody else over a session.
The two methods are the whole of what a plan needs, and they are the shape a session already has: read the map, or change it and say what changed.