1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
//! # Foreign Function Interface
//! `codemp` aims to be available as a library from as many programming languages as possible.
//! To achieve this, we rely on Foreign Function Interface.
//!
//! ```no_run
//! # async {
//! use codemp::api::controller::{AsyncReceiver, AsyncSender}; // needed for send/recv trait methods
//!
//! // connect first, api.code.mp is managed by hexed.technology
//! let client = codemp::Client::connect(codemp::api::Config {
//! username: "mail@example.net".into(), password: "dont-use-this-password".into(),
//! ..Default::default()
//! }).await?;
//!
//! // create and join a workspace
//! client.create_workspace("some-workspace").await?;
//! let workspace = client.attach_workspace("some-workspace").await?;
//!
//! // create a new buffer in this workspace and attach to it
//! workspace.create_buffer("/my/file.txt").await?;
//! let buffer = workspace.attach_buffer("/my/file.txt").await?;
//!
//! // write `hello!` at the beginning of this buffer
//! buffer.send(codemp::api::TextChange {
//! start_idx: 0, end_idx: 0,
//! content: "hello!".to_string(),
//! })?;
//!
//! // wait for cursor movements
//! loop {
//! let event = workspace.cursor().recv().await?;
//! println!("user {} moved on buffer {}", event.user, event.sel.buffer);
//! }
//! # Ok::<(),Box<dyn std::error::Error>>(())
//! # };
//! ```
//!
//! ## JavaScript
//! Our JavaScript glue is built with [`napi`](https://napi.rs).
//!
//! All async operations are handled on a separate tokio runtime, automatically managed by `napi`.
//! Callbacks are safely scheduled to be called on the main loop thread.
//!
//! ```js
//! import * as codemp from 'codemp';
//!
//! // connect first, api.code.mp is managed by hexed.technology
//! let client = await codemp.connect({
//! username: "mail@example.net",
//! password: "dont-use-this-password"
//! });
//!
//! // create and join a workspace
//! await client.createWorkspace("some-workspace");
//! let workspace = await client.attachWorkspace("some-workspace");
//!
//! // create a new buffer in this workspace and attach to it
//! await workspace.createBuffer("/my/file.txt");
//! let buffer = await workspace.attachBuffer("/my/file.txt");
//!
//! // write `hello!` at the beginning of this buffer
//! await buffer.send({
//! start_idx: 0, end_idx: 0,
//! content: "hello!",
//! });
//!
//! // wait for cursor movements
//! while (true) {
//! let event = await workspace.cursor().recv();
//! console.log(`user ${event.user} moved on buffer ${event.buffer}`);
//! }
//! ```
//!
//! ## Python
//! Our Python glue is built with [`PyO3`](https://pyo3.rs).
//!
//! All async operations return a `Promise`, which can we `.wait()`-ed to block and get the return
//! value. The `Future` itself is run on a `tokio` runtime in a dedicated thread, which must be
//! stared with `codemp.init()` before doing any async operations.
//!
//! ```py
//! import codemp
//!
//! # connect first, api.code.mp is managed by hexed.technology
//! client = codemp.connect(codemp.Config(
//! username = "mail@example.net",
//! password = "dont-use-this-password"
//! )).wait()
//!
//! # create and join a workspace
//! client.create_workspace("some-workspace").wait()
//! workspace = client.attach_workspace("some-workspace").wait()
//!
//! # create a new buffer in this workspace and attach to it
//! workspace.create_buffer("/my/file.txt").wait()
//! buffer = workspace.attach_buffer("/my/file.txt").wait()
//!
//! # write `hello!` at the beginning of this buffer
//! buffer.send(codemp.TextChange(
//! start_idx=0, end_idx=0,
//! content="hello!"
//! )).wait()
//!
//! # wait for cursor movements
//! while true:
//! event = workspace.cursor().recv().wait()
//! print(f"user {event.user} moved on buffer {event.buffer}")
//!
//! ```
//!
//! ## Lua
//! Our Lua glue is built with [`mlua`](https://github.com/mlua-rs/mlua).
//!
//! Lua bindings run all async code on a current thread tokio runtime, which should be driven with
//! a dedicated thread.
//!
//! All async functions will return a `Promise`, which can be `:await()`-ed to block and get the
//! return value.
//!
//! Note as Lua uses filename to locate entrypoint symbol, so shared object can't just have any name.
//! Accepted filenames are `libcodemp.___`, `codemp.___`, `codemp_native.___`, `codemp_lua.___` (extension depends on your platform: `so` on linux, `dll` on windows, `dylib` on macos).
//! Type hints are provided in `dist/lua/annotations.lua`, just include them in your language server: `---@module 'annotations'`.
//!
//! `codemp` is available as a rock on [LuaRocks](https://luarocks.org/modules/alemi/codemp),
//! however LuaRocks compiles from source and will require having `cargo` installed.
//! We provide pre-built binaries at [codemp.dev/releases/lua](https://codemp.dev/releases/lua/).
//! **Please do not rely on this link, as our built binaries will likely move somewhere else soon!**.
//!
//! ```lua
//! CODEMP = require('codemp')
//!
//! -- connect first, api.code.mp is managed by hexed.technology
//! local client = CODEMP.connect({
//! username = "mail@example.net",
//! password = "dont-use-this-password"
//! }):await()
//!
//! -- create and join a workspace
//! client:create_workspace("my-workspace"):await()
//! local workspace = client:attach_workspace("my-workspace"):await()
//!
//! -- create a new buffer in this workspace and attach to it
//! workspace:create_buffer("/my/file.txt"):await()
//! local buffer = workspace:attach_buffer("/my/file.txt"):await()
//!
//! -- write `hello!` at the beginning of this buffer
//! buffer:send({
//! start_idx = 0, end_idx = 0,
//! content = "hello!"
//! }):await()
//!
//! -- wait for cursor movements
//! while true do
//! local event = workspace.cursor:recv():await()
//! print("user " .. event.user .. " moved on buffer " .. event.buffer)
//! end
//! ```
//!
//! ## Java
//! Our Java glue is built with [`jni`](https://github.com/jni-rs/jni-rs).
//!
//! Memory management is entirely delegated to the JVM's garbage collector.
//! A more elegant solution than `Object.finalize()`, who is deprecated in newer Java versions, may be coming eventually.
//!
//! Exceptions coming from the native side have generally been made checked to imitate Rust's philosophy with `Result`.
//! `JNIException`s are however unchecked: there is nothing you can do to recover from them, as they usually represent a severe error in the glue code. If they arise, it's probably a bug.
//!
//! ```java
//! import mp.code.*;
//!
//! // connect first, api.code.mp is managed by hexed.technology
//! Client client = Client.connect(new data.Config(
//! "mail@example.net",
//! "dont-use-this-password"
//! ));
//!
//! // create and join a workspace
//! client.createWorkspace("some-workspace");
//! Workspace workspace = client.attachWorkspace("some-workspace");
//!
//! // create a new buffer in this workspace and attach to it
//! workspace.createBuffer("/my/file.txt");
//! BufferController buffer = workspace.attachBuffer("/my/file.txt");
//!
//! // write `hello!` at the beginning of this buffer
//! buffer.send(new data.TextChange(
//! 0, 0, "hello!",
//! java.util.OptionalLong.empty() // optional, used for error detection
//! ));
//!
//! // wait for cursor movements
//! while (true) {
//! data.Cursor event = workspace.cursor().recv();
//! System.out.printf("user %s moved on buffer %s\n", event.user, event.buffer);
//! }
//! ```
/// java bindings, built with [jni]
/// lua bindings, built with [mlua]
/// javascript bindings, built with [napi]
/// python bindings, built with [pyo3]