chore: split list_workspaces, renamed filetree, refactored fetch_users and fetch_buffers

Co-authored-by: alemi <me@alemi.dev>
This commit is contained in:
zaaarf 2024-10-16 00:42:55 +02:00
parent 6f04c38779
commit e5fd0ca76a
No known key found for this signature in database
GPG key ID: 102E445F4C3F829B
21 changed files with 239 additions and 183 deletions

View file

@ -91,17 +91,26 @@ public final class Client {
invite_to_workspace(this.ptr, workspaceId, user); invite_to_workspace(this.ptr, workspaceId, user);
} }
private static native String[] list_workspaces(long self, boolean owned, boolean invited) throws ConnectionRemoteException; private static native String[] fetch_owned_workspaces(long self) throws ConnectionRemoteException;
/** /**
* Lists available workspaces according to certain filters. * Lists workspaces owned by the current user.
* @param owned if owned workspaces should be included
* @param invited if workspaces the user is invited to should be included
* @return an array of workspace IDs * @return an array of workspace IDs
* @throws ConnectionRemoteException if an error occurs in communicating with the server * @throws ConnectionRemoteException if an error occurs in communicating with the server
*/ */
public String[] listWorkspaces(boolean owned, boolean invited) throws ConnectionRemoteException { public String[] fetchOwnedWorkspaces() throws ConnectionRemoteException {
return list_workspaces(this.ptr, owned, invited); return fetch_owned_workspaces(this.ptr);
}
private static native String[] fetch_joined_workspaces(long self) throws ConnectionRemoteException;
/**
* Lists workspaces the current user has joined.
* @return an array of workspace IDs
* @throws ConnectionRemoteException if an error occurs in communicating with the server
*/
public String[] fetchJoinedWorkspaces() throws ConnectionRemoteException {
return fetch_joined_workspaces(this.ptr);
} }
private static native String[] active_workspaces(long self); private static native String[] active_workspaces(long self);

View file

@ -1,10 +1,10 @@
package mp.code; package mp.code;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import lombok.Getter; import lombok.Getter;
import mp.code.data.User;
import mp.code.exceptions.ConnectionException; import mp.code.exceptions.ConnectionException;
import mp.code.exceptions.ConnectionRemoteException; import mp.code.exceptions.ConnectionRemoteException;
import mp.code.exceptions.ControllerException; import mp.code.exceptions.ControllerException;
@ -57,17 +57,16 @@ public final class Workspace {
return Optional.ofNullable(get_buffer(this.ptr, path)); return Optional.ofNullable(get_buffer(this.ptr, path));
} }
private static native String[] filetree(long self, String filter, boolean strict); private static native String[] search_buffers(long self, String filter);
/** /**
* Gets the file tree for this workspace, optionally filtering it. * Searches for buffers matching the filter in this workspace.
* @param filter applies an optional filter to the outputs * @param filter the filter to apply
* @param strict whether it should be a strict match (equals) or not (startsWith)
* @return an array containing file tree as flat paths * @return an array containing file tree as flat paths
*/ */
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public String[] filetree(Optional<String> filter, boolean strict) { public String[] searchBuffers(Optional<String> filter) {
return filetree(this.ptr, filter.orElse(null), strict); return search_buffers(this.ptr, filter.orElse(null));
} }
private static native String[] active_buffers(long self); private static native String[] active_buffers(long self);
@ -125,37 +124,39 @@ public final class Workspace {
return detach_buffer(this.ptr, path); return detach_buffer(this.ptr, path);
} }
private static native void fetch_buffers(long self) throws ConnectionRemoteException; private static native String[] fetch_buffers(long self) throws ConnectionRemoteException;
/** /**
* Updates the local list of buffers. * Updates and fetches the local list of buffers.
* @return the updated list
* @throws ConnectionRemoteException if an error occurs in communicating with the server * @throws ConnectionRemoteException if an error occurs in communicating with the server
*/ */
public void fetchBuffers() throws ConnectionRemoteException { public String[] fetchBuffers() throws ConnectionRemoteException {
fetch_buffers(this.ptr); return fetch_buffers(this.ptr);
} }
private static native void fetch_users(long self) throws ConnectionRemoteException; private static native User[] fetch_users(long self) throws ConnectionRemoteException;
/** /**
* Updates the local list of users. * Updates and fetches the local list of users.
* @return the updated list
* @throws ConnectionRemoteException if an error occurs in communicating with the server * @throws ConnectionRemoteException if an error occurs in communicating with the server
*/ */
public void fetchUsers() throws ConnectionRemoteException { public User[] fetchUsers() throws ConnectionRemoteException {
fetch_buffers(this.ptr); return fetch_users(this.ptr);
} }
private static native UUID[] list_buffer_users(long self, String path) throws ConnectionRemoteException; private static native User[] fetch_buffer_users(long self, String path) throws ConnectionRemoteException;
/** /**
* Lists the user attached to a certain buffer. * Fetches the users attached to a certain buffer.
* The user must be attached to the buffer to perform this operation. * The user must be attached to the buffer to perform this operation.
* @param path the path of the buffer to search * @param path the path of the buffer to search
* @return an array of user {@link UUID UUIDs} * @return an array of {@link User}s
* @throws ConnectionRemoteException if an error occurs in communicating with the server, or the user wasn't attached * @throws ConnectionRemoteException if an error occurs in communicating with the server, or the user wasn't attached
*/ */
public UUID[] listBufferUsers(String path) throws ConnectionRemoteException { public User[] fetchBufferUsers(String path) throws ConnectionRemoteException {
return list_buffer_users(this.ptr, path); return fetch_buffer_users(this.ptr, path);
} }
private static native void delete_buffer(long self, String path) throws ConnectionRemoteException; private static native void delete_buffer(long self, String path) throws ConnectionRemoteException;

View file

@ -158,6 +158,17 @@ function MaybeBufferUpdatePromise:cancel() end
---invoke callback asynchronously as soon as promise is ready ---invoke callback asynchronously as soon as promise is ready
function MaybeBufferUpdatePromise:and_then(cb) end function MaybeBufferUpdatePromise:and_then(cb) end
---@class (exact) UserListPromise : Promise
local UserListPromise = {}
--- block until promise is ready and return value
--- @return User[]
function UserListPromise:await() end
--- cancel promise execution
function UserListPromise:cancel() end
---@param cb fun(x: User[]) callback to invoke
---invoke callback asynchronously as soon as promise is ready
function UserListPromise:and_then(cb) end
-- [[ END ASYNC STUFF ]] -- [[ END ASYNC STUFF ]]
@ -212,13 +223,17 @@ function Client:delete_workspace(ws) end
---grant user acccess to workspace ---grant user acccess to workspace
function Client:invite_to_workspace(ws, user) end function Client:invite_to_workspace(ws, user) end
---@param owned boolean? list owned workspaces, default true
---@param invited boolean? list invited workspaces, default true
---@return StringArrayPromise ---@return StringArrayPromise
---@async ---@async
---@nodiscard ---@nodiscard
---grant user acccess to workspace ---fetch and list owned workspaces
function Client:list_workspaces(owned, invited) end function Client:fetch_owned_workspaces() end
---@return StringArrayPromise
---@async
---@nodiscard
---fetch and list joined workspaces
function Client:fetch_joined_workspaces() end
---@param ws string workspace id to get ---@param ws string workspace id to get
---@return Workspace? ---@return Workspace?
@ -281,10 +296,9 @@ function Workspace:attach_buffer(path) end
function Workspace:detach_buffer(path) end function Workspace:detach_buffer(path) end
---@param filter? string apply a filter to the return elements ---@param filter? string apply a filter to the return elements
---@param strict? boolean whether to strictly match or just check whether it starts with it
---@return string[] ---@return string[]
---return the list of available buffers in this workspace, as relative paths from workspace root ---return the list of available buffers in this workspace, as relative paths from workspace root
function Workspace:filetree(filter, strict) end function Workspace:search_buffers(filter) end
---@return User[] ---@return User[]
---return all names of users currently in this workspace ---return all names of users currently in this workspace
@ -302,6 +316,13 @@ function Workspace:fetch_buffers(path) end
---force refresh users list from workspace ---force refresh users list from workspace
function Workspace:fetch_users(path) end function Workspace:fetch_users(path) end
---@param path string the buffer to look in
---@return UserListPromise
---@async
---@nodiscard
---fetch the list of users in the given buffer
function Workspace:fetch_buffer_users(path) end
---@class (exact) WorkspaceEvent ---@class (exact) WorkspaceEvent
---@field type string ---@field type string
---@field value string ---@field value string

View file

@ -51,7 +51,8 @@ class Client:
def create_workspace(self, workspace: str) -> Promise[None]: ... def create_workspace(self, workspace: str) -> Promise[None]: ...
def delete_workspace(self, workspace: str) -> Promise[None]: ... def delete_workspace(self, workspace: str) -> Promise[None]: ...
def invite_to_workspace(self, workspace: str, username: str) -> Promise[None]: ... def invite_to_workspace(self, workspace: str, username: str) -> Promise[None]: ...
def list_workspaces(self, owned: bool, invited: bool) -> Promise[list[str]]: ... def fetch_owned_workspaces(self) -> Promise[list[str]]: ...
def fetch_joined_workspaces(self) -> Promise[list[str]]: ...
def leave_workspace(self, workspace: str) -> bool: ... def leave_workspace(self, workspace: str) -> bool: ...
def get_workspace(self, id: str) -> Workspace: ... def get_workspace(self, id: str) -> Workspace: ...
def active_workspaces(self) -> list[str]: ... def active_workspaces(self) -> list[str]: ...
@ -69,16 +70,16 @@ class Workspace:
def create_buffer(self, path: str) -> Promise[None]: ... def create_buffer(self, path: str) -> Promise[None]: ...
def attach_buffer(self, path: str) -> Promise[BufferController]: ... def attach_buffer(self, path: str) -> Promise[BufferController]: ...
def detach_buffer(self, path: str) -> bool: ... def detach_buffer(self, path: str) -> bool: ...
def fetch_buffers(self) -> Promise[None]: ... def fetch_buffers(self) -> Promise[list[str]]: ...
def fetch_users(self) -> Promise[None]: ... def fetch_users(self) -> Promise[list[User]]: ...
def list_buffer_users(self, path: str) -> Promise[list[str]]: ... def fetch_buffer_users(self, path: str) -> Promise[list[User]]: ...
def delete_buffer(self, path: str) -> Promise[None]: ... def delete_buffer(self, path: str) -> Promise[None]: ...
def id(self) -> str: ... def id(self) -> str: ...
def cursor(self) -> CursorController: ... def cursor(self) -> CursorController: ...
def get_buffer(self, path: str) -> Optional[BufferController]: ... def get_buffer(self, path: str) -> Optional[BufferController]: ...
def user_list(self) -> list[User]: ... def user_list(self) -> list[User]: ...
def active_buffers(self) -> list[str]: ... def active_buffers(self) -> list[str]: ...
def filetree(self, filter: Optional[str], strict: bool) -> list[str]: ... def search_buffers(self, filter: Optional[str]) -> list[str]: ...
def recv(self) -> Promise[Event]: ... def recv(self) -> Promise[Event]: ...
def try_recv(self) -> Promise[Optional[Event]]: ... def try_recv(self) -> Promise[Optional[Event]]: ...
def poll(self) -> Promise[None]: ... def poll(self) -> Promise[None]: ...

View file

@ -19,11 +19,9 @@ pub mod event;
/// data structure for remote users /// data structure for remote users
pub mod user; pub mod user;
pub use change::BufferUpdate; pub use change::{BufferUpdate, TextChange};
pub use change::TextChange;
pub use config::Config; pub use config::Config;
pub use controller::Controller; pub use controller::{AsyncReceiver, AsyncSender, Controller};
pub use cursor::Cursor; pub use cursor::{Cursor, Selection};
pub use cursor::Selection;
pub use event::Event; pub use event::Event;
pub use user::User; pub use user::User;

View file

@ -170,8 +170,11 @@ impl BufferWorker {
// in case we have a "replace" span // in case we have a "replace" span
if change.is_delete() { if change.is_delete() {
self.branch self.branch.delete_without_content(
.delete_without_content(&mut self.oplog, self.agent_id, clip_start..clip_end); &mut self.oplog,
self.agent_id,
clip_start..clip_end,
);
} }
if change.is_insert() { if change.is_insert() {
@ -247,7 +250,9 @@ impl BufferWorker {
{ {
tracing::warn!( tracing::warn!(
"Insert span ({}, {}) differs from effective content len ({})", "Insert span ({}, {}) differs from effective content len ({})",
dtop.start(), dtop.end(), dtop.content_as_str().unwrap_or_default().len() dtop.start(),
dtop.end(),
dtop.content_as_str().unwrap_or_default().len()
); );
} }
crate::api::BufferUpdate { crate::api::BufferUpdate {

View file

@ -130,9 +130,18 @@ impl Client {
Ok(()) Ok(())
} }
/// List all available workspaces, also filtering between those owned and those invited to. /// Fetch the names of all workspaces owned by the current user.
pub async fn list_workspaces(&self, owned: bool, invited: bool) -> RemoteResult<Vec<String>> { pub async fn fetch_owned_workspaces(&self) -> RemoteResult<Vec<String>> {
let mut workspaces = self self.fetch_workspaces(true).await
}
/// Fetch the names of all workspaces the current user has joined.
pub async fn fetch_joined_workspaces(&self) -> RemoteResult<Vec<String>> {
self.fetch_workspaces(false).await
}
async fn fetch_workspaces(&self, owned: bool) -> RemoteResult<Vec<String>> {
let workspaces = self
.0 .0
.session .session
.clone() .clone()
@ -140,20 +149,18 @@ impl Client {
.await? .await?
.into_inner(); .into_inner();
let mut out = Vec::new();
if owned { if owned {
out.append(&mut workspaces.owned) Ok(workspaces.owned)
} else {
Ok(workspaces.invited)
} }
if invited {
out.append(&mut workspaces.invited)
}
Ok(out)
} }
/// Join and return a [`Workspace`]. /// Join and return a [`Workspace`].
pub async fn attach_workspace(&self, workspace: impl AsRef<str>) -> ConnectionResult<Workspace> { pub async fn attach_workspace(
&self,
workspace: impl AsRef<str>,
) -> ConnectionResult<Workspace> {
let token = self let token = self
.0 .0
.session .session

View file

@ -2,10 +2,7 @@ use jni::{objects::JObject, JNIEnv};
use jni_toolbox::jni; use jni_toolbox::jni;
use crate::{ use crate::{
api::{ api::{AsyncReceiver, AsyncSender, BufferUpdate, TextChange},
controller::{AsyncReceiver, AsyncSender},
BufferUpdate, TextChange,
},
errors::ControllerError, errors::ControllerError,
}; };

View file

@ -46,14 +46,16 @@ fn invite_to_workspace(
super::tokio().block_on(client.invite_to_workspace(workspace, user)) super::tokio().block_on(client.invite_to_workspace(workspace, user))
} }
/// List available workspaces. /// List owned workspaces.
#[jni(package = "mp.code", class = "Client")] #[jni(package = "mp.code", class = "Client")]
fn list_workspaces( fn fetch_owned_workspaces(client: &mut Client) -> Result<Vec<String>, RemoteError> {
client: &mut Client, super::tokio().block_on(client.fetch_owned_workspaces())
owned: bool, }
invited: bool,
) -> Result<Vec<String>, RemoteError> { /// List joined workspaces.
super::tokio().block_on(client.list_workspaces(owned, invited)) #[jni(package = "mp.code", class = "Client")]
fn fetch_joined_workspaces(client: &mut Client) -> Result<Vec<String>, RemoteError> {
super::tokio().block_on(client.fetch_joined_workspaces())
} }
/// List available workspaces. /// List available workspaces.

View file

@ -1,8 +1,5 @@
use crate::{ use crate::{
api::{ api::{AsyncReceiver, AsyncSender, Cursor, Selection},
controller::{AsyncReceiver, AsyncSender},
Cursor, Selection,
},
errors::ControllerError, errors::ControllerError,
}; };
use jni::{objects::JObject, JNIEnv}; use jni::{objects::JObject, JNIEnv};

View file

@ -25,10 +25,10 @@ fn get_buffer(workspace: &mut Workspace, path: String) -> Option<crate::buffer::
workspace.get_buffer(&path) workspace.get_buffer(&path)
} }
/// Get the filetree. /// Searches for buffers matching the filter.
#[jni(package = "mp.code", class = "Workspace")] #[jni(package = "mp.code", class = "Workspace")]
fn filetree(workspace: &mut Workspace, filter: Option<String>, strict: bool) -> Vec<String> { fn search_buffers(workspace: &mut Workspace, filter: Option<String>) -> Vec<String> {
workspace.filetree(filter.as_deref(), strict) workspace.search_buffers(filter.as_deref())
} }
/// Gets a list of the active buffers. /// Gets a list of the active buffers.
@ -66,23 +66,23 @@ fn detach_buffer(workspace: &mut Workspace, path: String) -> bool {
/// Update the local buffer list. /// Update the local buffer list.
#[jni(package = "mp.code", class = "Workspace")] #[jni(package = "mp.code", class = "Workspace")]
fn fetch_buffers(workspace: &mut Workspace) -> Result<(), RemoteError> { fn fetch_buffers(workspace: &mut Workspace) -> Result<Vec<String>, RemoteError> {
super::tokio().block_on(workspace.fetch_buffers()) super::tokio().block_on(workspace.fetch_buffers())
} }
/// Update the local user list. /// Update the local user list.
#[jni(package = "mp.code", class = "Workspace")] #[jni(package = "mp.code", class = "Workspace")]
fn fetch_users(workspace: &mut Workspace) -> Result<(), RemoteError> { fn fetch_users(workspace: &mut Workspace) -> Result<Vec<User>, RemoteError> {
super::tokio().block_on(workspace.fetch_users()) super::tokio().block_on(workspace.fetch_users())
} }
/// List users attached to a buffer. /// Fetch users attached to a buffer.
#[jni(package = "mp.code", class = "Workspace")] #[jni(package = "mp.code", class = "Workspace")]
fn list_buffer_users( fn fetch_buffer_users(
workspace: &mut Workspace, workspace: &mut Workspace,
path: String, path: String,
) -> Result<Vec<crate::api::User>, RemoteError> { ) -> Result<Vec<crate::api::User>, RemoteError> {
super::tokio().block_on(workspace.list_buffer_users(&path)) super::tokio().block_on(workspace.fetch_buffer_users(&path))
} }
/// Delete a buffer. /// Delete a buffer.

View file

@ -46,14 +46,16 @@ impl Client {
Ok(self.delete_workspace(workspace).await?) Ok(self.delete_workspace(workspace).await?)
} }
#[napi(js_name = "listWorkspaces")] #[napi(js_name = "fetchOwnedWorkspaces")]
/// list available workspaces /// fetch owned workspaces
pub async fn js_list_workspaces( pub async fn js_fetch_owned_workspaces(&self) -> napi::Result<Vec<String>> {
&self, Ok(self.fetch_owned_workspaces().await?)
owned: bool, }
invited: bool,
) -> napi::Result<Vec<String>> { #[napi(js_name = "fetchJoinedWorkspaces")]
Ok(self.list_workspaces(owned, invited).await?) /// fetch joined workspaces
pub async fn js_fetch_joined_workspaces(&self) -> napi::Result<Vec<String>> {
Ok(self.fetch_joined_workspaces().await?)
} }
#[napi(js_name = "inviteToWorkspace")] #[napi(js_name = "inviteToWorkspace")]

View file

@ -44,9 +44,9 @@ impl Workspace {
} }
/// List all available buffers in this workspace /// List all available buffers in this workspace
#[napi(js_name = "filetree")] #[napi(js_name = "search_buffers")]
pub fn js_filetree(&self, filter: Option<&str>, strict: bool) -> Vec<String> { pub fn js_search_buffers(&self, filter: Option<&str>) -> Vec<String> {
self.filetree(filter, strict) self.search_buffers(filter)
} }
/// List all user names currently in this workspace /// List all user names currently in this workspace
@ -121,7 +121,7 @@ impl Workspace {
})?; })?;
self.callback(move |controller: Workspace| { self.callback(move |controller: Workspace| {
tsfn.call(controller.clone(), ThreadsafeFunctionCallMode::Blocking); //check this with tracing also we could use Ok(event) to get the error tsfn.call(controller.clone(), ThreadsafeFunctionCallMode::Blocking); //check this with tracing also we could use Ok(event) to get the error
// If it blocks the main thread too many time we have to change this // If it blocks the main thread too many time we have to change this
}); });
Ok(()) Ok(())
@ -137,23 +137,28 @@ impl Workspace {
/// Re-fetch remote buffer list /// Re-fetch remote buffer list
#[napi(js_name = "fetchBuffers")] #[napi(js_name = "fetchBuffers")]
pub async fn js_fetch_buffers(&self) -> napi::Result<()> { pub async fn js_fetch_buffers(&self) -> napi::Result<Vec<String>> {
Ok(self.fetch_buffers().await?) Ok(self.fetch_buffers().await?)
} }
/// Re-fetch the list of all users in the workspace. /// Re-fetch the list of all users in the workspace.
#[napi(js_name = "fetchUsers")] #[napi(js_name = "fetchUsers")]
pub async fn js_fetch_users(&self) -> napi::Result<()> { pub async fn js_fetch_users(&self) -> napi::Result<Vec<JsUser>> {
Ok(self.fetch_users().await?) Ok(self
.fetch_users()
.await?
.into_iter()
.map(JsUser::from)
.collect())
} }
/// List users attached to a specific buffer /// List users attached to a specific buffer
#[napi(js_name = "listBufferUsers")] #[napi(js_name = "fetchBufferUsers")]
pub async fn js_list_buffer_users( pub async fn js_fetch_buffer_users(
&self, &self,
path: String, path: String,
) -> napi::Result<Vec<crate::ffi::js::client::JsUser>> { ) -> napi::Result<Vec<crate::ffi::js::client::JsUser>> {
Ok(self Ok(self
.list_buffer_users(&path) .fetch_buffer_users(&path)
.await? .await?
.into_iter() .into_iter()
.map(super::client::JsUser::from) .map(super::client::JsUser::from)

View file

@ -12,8 +12,12 @@ impl LuaUserData for CodempClient {
Ok(format!("{:?}", this)) Ok(format!("{:?}", this))
}); });
methods.add_method("current_user", |_, this, ()| Ok(this.current_user().clone())); methods.add_method("current_user", |_, this, ()| {
methods.add_method("active_workspaces", |_, this, ()| Ok(this.active_workspaces())); Ok(this.current_user().clone())
});
methods.add_method("active_workspaces", |_, this, ()| {
Ok(this.active_workspaces())
});
methods.add_method( methods.add_method(
"refresh", "refresh",
@ -39,8 +43,14 @@ impl LuaUserData for CodempClient {
a_sync! { this => this.invite_to_workspace(ws, user).await? } a_sync! { this => this.invite_to_workspace(ws, user).await? }
); );
methods.add_method("list_workspaces", |_, this, (owned,invited):(Option<bool>,Option<bool>)| methods.add_method(
a_sync! { this => this.list_workspaces(owned.unwrap_or(true), invited.unwrap_or(true)).await? } "fetch_owned_workspaces",
|_, this, ()| a_sync! { this => this.fetch_owned_workspaces().await? },
);
methods.add_method(
"fetch_joined_workspaces",
|_, this, ()| a_sync! { this => this.fetch_joined_workspaces().await? },
); );
methods.add_method("leave_workspace", |_, this, (ws,): (String,)| { methods.add_method("leave_workspace", |_, this, (ws,): (String,)| {

View file

@ -101,6 +101,7 @@ macro_rules! callback_args {
callback_args! { callback_args! {
Str: String, Str: String,
VecStr: Vec<String>, VecStr: Vec<String>,
VecUser: Vec<CodempUser>,
Client: CodempClient, Client: CodempClient,
CursorController: CodempCursorController, CursorController: CodempCursorController,
BufferController: CodempBufferController, BufferController: CodempBufferController,

View file

@ -43,12 +43,15 @@ impl LuaUserData for CodempWorkspace {
|_, this, ()| a_sync! { this => this.fetch_users().await? }, |_, this, ()| a_sync! { this => this.fetch_users().await? },
); );
methods.add_method( methods.add_method("search_buffers", |_, this, (filter,): (Option<String>,)| {
"filetree", Ok(this.search_buffers(filter.as_deref()))
|_, this, (filter, strict): (Option<String>, Option<bool>)| { });
Ok(this.filetree(filter.as_deref(), strict.unwrap_or(false)))
}, methods.add_method("fetch_buffer_users", |_, this, (path,): (String,)| {
); a_sync! {
this => this.fetch_buffer_users(&path).await?
}
});
methods.add_method("id", |_, this, ()| Ok(this.id())); methods.add_method("id", |_, this, ()| Ok(this.id()));
methods.add_method("cursor", |_, this, ()| Ok(this.cursor())); methods.add_method("cursor", |_, this, ()| Ok(this.cursor()));

View file

@ -5,26 +5,26 @@
//! ```no_run //! ```no_run
//! # async { //! # async {
//! use codemp::api::controller::{AsyncReceiver, AsyncSender}; // needed for send/recv trait methods //! use codemp::api::controller::{AsyncReceiver, AsyncSender}; // needed for send/recv trait methods
//! //!
//! // connect first, api.code.mp is managed by hexed.technology //! // connect first, api.code.mp is managed by hexed.technology
//! let client = codemp::Client::connect(codemp::api::Config::new( //! let client = codemp::Client::connect(codemp::api::Config::new(
//! "mail@example.net", "dont-use-this-password" //! "mail@example.net", "dont-use-this-password"
//! )).await?; //! )).await?;
//! //!
//! // create and join a workspace //! // create and join a workspace
//! client.create_workspace("some-workspace").await?; //! client.create_workspace("some-workspace").await?;
//! let workspace = client.join_workspace("some-workspace").await?; //! let workspace = client.join_workspace("some-workspace").await?;
//! //!
//! // create a new buffer in this workspace and attach to it //! // create a new buffer in this workspace and attach to it
//! workspace.create("/my/file.txt").await?; //! workspace.create("/my/file.txt").await?;
//! let buffer = workspace.attach("/my/file.txt").await?; //! let buffer = workspace.attach("/my/file.txt").await?;
//! //!
//! // write `hello!` at the beginning of this buffer //! // write `hello!` at the beginning of this buffer
//! buffer.send(codemp::api::TextChange { //! buffer.send(codemp::api::TextChange {
//! start: 0, end: 0, //! start: 0, end: 0,
//! content: "hello!".to_string(), //! content: "hello!".to_string(),
//! })?; //! })?;
//! //!
//! // wait for cursor movements //! // wait for cursor movements
//! loop { //! loop {
//! let event = workspace.cursor().recv().await?; //! let event = workspace.cursor().recv().await?;
@ -42,26 +42,26 @@
//! //!
//! ```js //! ```js
//! import * as codemp from 'codemp'; //! import * as codemp from 'codemp';
//! //!
//! // connect first, api.code.mp is managed by hexed.technology //! // connect first, api.code.mp is managed by hexed.technology
//! let client = await codemp.connect({ //! let client = await codemp.connect({
//! username: "mail@example.net", password: "dont-use-this-password" //! username: "mail@example.net", password: "dont-use-this-password"
//! }); //! });
//! //!
//! // create and join a workspace //! // create and join a workspace
//! await client.create_workspace("some-workspace"); //! await client.create_workspace("some-workspace");
//! let workspace = await client.join_workspace("some-workspace"); //! let workspace = await client.join_workspace("some-workspace");
//! //!
//! // create a new buffer in this workspace and attach to it //! // create a new buffer in this workspace and attach to it
//! await workspace.create("/my/file.txt"); //! await workspace.create("/my/file.txt");
//! let buffer = await workspace.attach("/my/file.txt"); //! let buffer = await workspace.attach("/my/file.txt");
//! //!
//! // write `hello!` at the beginning of this buffer //! // write `hello!` at the beginning of this buffer
//! await buffer.send({ //! await buffer.send({
//! start: 0, end: 0, //! start: 0, end: 0,
//! content: "hello!", //! content: "hello!",
//! }); //! });
//! //!
//! // wait for cursor movements //! // wait for cursor movements
//! while (true) { //! while (true) {
//! let event = await workspace.cursor().recv(); //! let event = await workspace.cursor().recv();
@ -78,26 +78,26 @@
//! //!
//! ```py //! ```py
//! import codemp //! import codemp
//! //!
//! # connect first, api.code.mp is managed by hexed.technology //! # connect first, api.code.mp is managed by hexed.technology
//! config = codemp.get_default_config() //! config = codemp.get_default_config()
//! config.username = "mail@example.net" //! config.username = "mail@example.net"
//! config.password = "dont-use-this-password" //! config.password = "dont-use-this-password"
//! client = codemp.connect(config).wait() //! client = codemp.connect(config).wait()
//! //!
//! # create and join a workspace //! # create and join a workspace
//! client.create_workspace("some-workspace").wait() //! client.create_workspace("some-workspace").wait()
//! workspace = client.join_workspace("some-workspace").wait() //! workspace = client.join_workspace("some-workspace").wait()
//! //!
//! # create a new buffer in this workspace and attach to it //! # create a new buffer in this workspace and attach to it
//! workspace.create("/my/file.txt").wait() //! workspace.create("/my/file.txt").wait()
//! buffer = workspace.attach("/my/file.txt").wait() //! buffer = workspace.attach("/my/file.txt").wait()
//! //!
//! # write `hello!` at the beginning of this buffer //! # write `hello!` at the beginning of this buffer
//! buffer.send( //! buffer.send(
//! 0, 0, "hello!" //! 0, 0, "hello!"
//! ).wait() //! ).wait()
//! //!
//! # wait for cursor movements //! # wait for cursor movements
//! while true: //! while true:
//! event = workspace.cursor().recv().wait() //! event = workspace.cursor().recv().wait()
@ -124,26 +124,26 @@
//! //!
//! ```lua //! ```lua
//! CODEMP = require('codemp') //! CODEMP = require('codemp')
//! //!
//! -- connect first, api.code.mp is managed by hexed.technology //! -- connect first, api.code.mp is managed by hexed.technology
//! local client = CODEMP.connect({ //! local client = CODEMP.connect({
//! username = "mail@example.net", password = "dont-use-this-password" //! username = "mail@example.net", password = "dont-use-this-password"
//! }):await() //! }):await()
//! //!
//! -- create and join a workspace //! -- create and join a workspace
//! client:create_workspace("my-workspace"):await() //! client:create_workspace("my-workspace"):await()
//! local workspace = client:join_workspace("my-workspace"):await() //! local workspace = client:join_workspace("my-workspace"):await()
//! //!
//! -- create a new buffer in this workspace and attach to it //! -- create a new buffer in this workspace and attach to it
//! workspace:create_buffer("/my/file.txt"):await() //! workspace:create_buffer("/my/file.txt"):await()
//! local buffer = workspace:attach_buffer("/my/file.txt"):await() //! local buffer = workspace:attach_buffer("/my/file.txt"):await()
//! //!
//! -- write `hello!` at the beginning of this buffer //! -- write `hello!` at the beginning of this buffer
//! buffer:send({ //! buffer:send({
//! start = 0, finish = 0, //! start = 0, finish = 0,
//! content = "hello!" //! content = "hello!"
//! }):await() //! }):await()
//! //!
//! -- wait for cursor movements //! -- wait for cursor movements
//! while true do //! while true do
//! local event = workspace.cursor:recv():await() //! local event = workspace.cursor:recv():await()
@ -162,26 +162,26 @@
//! //!
//! ```java //! ```java
//! import mp.code.*; //! import mp.code.*;
//! //!
//! // connect first, api.code.mp is managed by hexed.technology //! // connect first, api.code.mp is managed by hexed.technology
//! Client client = Client.connect( //! Client client = Client.connect(
//! new data.Config("mail@example.net", "dont-use-this-password") //! new data.Config("mail@example.net", "dont-use-this-password")
//! ); //! );
//! //!
//! // create and join a workspace //! // create and join a workspace
//! client.createWorkspace("some-workspace"); //! client.createWorkspace("some-workspace");
//! Workspace workspace = client.joinWorkspace("some-workspace"); //! Workspace workspace = client.joinWorkspace("some-workspace");
//! //!
//! // create a new buffer in this workspace and attach to it //! // create a new buffer in this workspace and attach to it
//! workspace.createBuffer("/my/file.txt"); //! workspace.createBuffer("/my/file.txt");
//! BufferController buffer = workspace.attachToBuffer("/my/file.txt"); //! BufferController buffer = workspace.attachToBuffer("/my/file.txt");
//! //!
//! // write `hello!` at the beginning of this buffer //! // write `hello!` at the beginning of this buffer
//! buffer.send(new data.TextChange( //! buffer.send(new data.TextChange(
//! 0, 0, "hello!", //! 0, 0, "hello!",
//! java.util.OptionalLong.empty() // optional, used for error detection //! java.util.OptionalLong.empty() // optional, used for error detection
//! )); //! ));
//! //!
//! // wait for cursor movements //! // wait for cursor movements
//! while (true) { //! while (true) {
//! data.Cursor event = workspace.getCursor().recv(); //! data.Cursor event = workspace.getCursor().recv();

View file

@ -55,16 +55,18 @@ impl Client {
a_sync_allow_threads!(py, this.invite_to_workspace(workspace, user).await) a_sync_allow_threads!(py, this.invite_to_workspace(workspace, user).await)
} }
#[pyo3(name = "list_workspaces")] #[pyo3(name = "fetch_owned_workspaces")]
fn pylist_workspaces( fn pyfetch_owned_workspaces(&self, py: Python<'_>) -> PyResult<super::Promise> {
&self, tracing::info!("attempting to fetch owned workspaces");
py: Python<'_>,
owned: bool,
invited: bool,
) -> PyResult<super::Promise> {
tracing::info!("attempting to list workspaces");
let this = self.clone(); let this = self.clone();
a_sync_allow_threads!(py, this.list_workspaces(owned, invited).await) a_sync_allow_threads!(py, this.fetch_owned_workspaces().await)
}
#[pyo3(name = "fetch_joined_workspaces")]
fn pyfetch_joined_workspaces(&self, py: Python<'_>) -> PyResult<super::Promise> {
tracing::info!("attempting to fetch joined workspaces");
let this = self.clone();
a_sync_allow_threads!(py, this.fetch_joined_workspaces().await)
} }
#[pyo3(name = "leave_workspace")] #[pyo3(name = "leave_workspace")]

View file

@ -41,11 +41,11 @@ impl Workspace {
a_sync_allow_threads!(py, this.fetch_users().await) a_sync_allow_threads!(py, this.fetch_users().await)
} }
#[pyo3(name = "list_buffer_users")] #[pyo3(name = "fetch_buffer_users")]
fn pylist_buffer_users(&self, py: Python, path: String) -> PyResult<Promise> { fn pyfetch_buffer_users(&self, py: Python, path: String) -> PyResult<Promise> {
// crate::Result<Vec<crate::api::User>> // crate::Result<Vec<crate::api::User>>
let this = self.clone(); let this = self.clone();
a_sync_allow_threads!(py, this.list_buffer_users(path.as_str()).await) a_sync_allow_threads!(py, this.fetch_buffer_users(path.as_str()).await)
} }
#[pyo3(name = "delete_buffer")] #[pyo3(name = "delete_buffer")]
@ -74,10 +74,10 @@ impl Workspace {
self.active_buffers() self.active_buffers()
} }
#[pyo3(name = "filetree")] #[pyo3(name = "search_buffers")]
#[pyo3(signature = (filter=None, strict=false))] #[pyo3(signature = (filter=None))]
fn pyfiletree(&self, filter: Option<&str>, strict: bool) -> Vec<String> { fn pysearch_buffers(&self, filter: Option<&str>) -> Vec<String> {
self.filetree(filter, strict) self.search_buffers(filter)
} }
#[pyo3(name = "user_list")] #[pyo3(name = "user_list")]

View file

@ -2,7 +2,7 @@
//! All-in-one renamed imports with `use codemp::prelude::*`. //! All-in-one renamed imports with `use codemp::prelude::*`.
pub use crate::api::{ pub use crate::api::{
controller::AsyncReceiver as CodempAsyncReceiver, controller::AsyncSender as CodempAsyncSender, AsyncReceiver as CodempAsyncReceiver, AsyncSender as CodempAsyncSender,
BufferUpdate as CodempBufferUpdate, Config as CodempConfig, Controller as CodempController, BufferUpdate as CodempBufferUpdate, Config as CodempConfig, Controller as CodempController,
Cursor as CodempCursor, Event as CodempEvent, Selection as CodempSelection, Cursor as CodempCursor, Event as CodempEvent, Selection as CodempSelection,
TextChange as CodempTextChange, User as CodempUser, TextChange as CodempTextChange, User as CodempUser,

View file

@ -26,7 +26,7 @@ use codemp_proto::{
}; };
use dashmap::{DashMap, DashSet}; use dashmap::{DashMap, DashSet};
use std::{collections::BTreeSet, sync::Arc}; use std::sync::Arc;
use tokio::sync::{mpsc, mpsc::error::TryRecvError}; use tokio::sync::{mpsc, mpsc::error::TryRecvError};
use tonic::Streaming; use tonic::Streaming;
use uuid::Uuid; use uuid::Uuid;
@ -201,45 +201,48 @@ impl Workspace {
} }
/// Re-fetch the list of available buffers in the workspace. /// Re-fetch the list of available buffers in the workspace.
pub async fn fetch_buffers(&self) -> RemoteResult<()> { pub async fn fetch_buffers(&self) -> RemoteResult<Vec<String>> {
let mut workspace_client = self.0.services.ws(); let mut workspace_client = self.0.services.ws();
let buffers = workspace_client let resp = workspace_client
.list_buffers(tonic::Request::new(Empty {})) .list_buffers(tonic::Request::new(Empty {}))
.await? .await?
.into_inner() .into_inner();
.buffers;
let mut out = Vec::new();
self.0.filetree.clear(); self.0.filetree.clear();
for b in buffers { for b in resp.buffers {
self.0.filetree.insert(b.path); self.0.filetree.insert(b.path.clone());
out.push(b.path);
} }
Ok(()) Ok(out)
} }
/// Re-fetch the list of all users in the workspace. /// Re-fetch the list of all users in the workspace.
pub async fn fetch_users(&self) -> RemoteResult<()> { pub async fn fetch_users(&self) -> RemoteResult<Vec<User>> {
let mut workspace_client = self.0.services.ws(); let mut workspace_client = self.0.services.ws();
let users = BTreeSet::from_iter( let users = workspace_client
workspace_client .list_users(tonic::Request::new(Empty {}))
.list_users(tonic::Request::new(Empty {})) .await?
.await? .into_inner()
.into_inner() .users
.users .into_iter()
.into_iter() .map(User::from);
.map(User::from),
); let mut result = Vec::new();
self.0.users.clear(); self.0.users.clear();
for u in users { for u in users {
self.0.users.insert(u.id, u); self.0.users.insert(u.id, u.clone());
result.push(u);
} }
Ok(()) Ok(result)
} }
/// Get a list of the [User]s attached to a specific buffer. /// Fetch a list of the [User]s attached to a specific buffer.
pub async fn list_buffer_users(&self, path: &str) -> RemoteResult<Vec<User>> { pub async fn fetch_buffer_users(&self, path: &str) -> RemoteResult<Vec<User>> {
let mut workspace_client = self.0.services.ws(); let mut workspace_client = self.0.services.ws();
let buffer_users = workspace_client let buffer_users = workspace_client
.list_buffer_users(tonic::Request::new(BufferNode { .list_buffer_users(tonic::Request::new(BufferNode {
@ -311,20 +314,12 @@ impl Workspace {
/// Get the filetree as it is currently cached. /// Get the filetree as it is currently cached.
/// A filter may be applied, and it may be strict (equality check) or not (starts_with check). /// A filter may be applied, and it may be strict (equality check) or not (starts_with check).
// #[cfg_attr(feature = "js", napi)] // https://github.com/napi-rs/napi-rs/issues/1120 // #[cfg_attr(feature = "js", napi)] // https://github.com/napi-rs/napi-rs/issues/1120
pub fn filetree(&self, filter: Option<&str>, strict: bool) -> Vec<String> { pub fn search_buffers(&self, filter: Option<&str>) -> Vec<String> {
let mut tree = self let mut tree = self
.0 .0
.filetree .filetree
.iter() .iter()
.filter(|f| { .filter(|f| filter.map_or(true, |flt| f.starts_with(flt)))
filter.map_or(true, |flt| {
if strict {
f.as_str() == flt
} else {
f.starts_with(flt)
}
})
})
.map(|f| f.clone()) .map(|f| f.clone())
.collect::<Vec<String>>(); .collect::<Vec<String>>();
tree.sort(); tree.sort();