diff --git a/dist/java/src/mp/code/Client.java b/dist/java/src/mp/code/Client.java index 1146973..f7d56f5 100644 --- a/dist/java/src/mp/code/Client.java +++ b/dist/java/src/mp/code/Client.java @@ -91,17 +91,26 @@ public final class Client { 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. - * @param owned if owned workspaces should be included - * @param invited if workspaces the user is invited to should be included + * Lists workspaces owned by the current user. * @return an array of workspace IDs * @throws ConnectionRemoteException if an error occurs in communicating with the server */ - public String[] listWorkspaces(boolean owned, boolean invited) throws ConnectionRemoteException { - return list_workspaces(this.ptr, owned, invited); + public String[] fetchOwnedWorkspaces() throws ConnectionRemoteException { + 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); diff --git a/dist/java/src/mp/code/Workspace.java b/dist/java/src/mp/code/Workspace.java index 1324f6a..b359516 100644 --- a/dist/java/src/mp/code/Workspace.java +++ b/dist/java/src/mp/code/Workspace.java @@ -1,10 +1,10 @@ package mp.code; import java.util.Optional; -import java.util.UUID; import java.util.function.Consumer; import lombok.Getter; +import mp.code.data.User; import mp.code.exceptions.ConnectionException; import mp.code.exceptions.ConnectionRemoteException; import mp.code.exceptions.ControllerException; @@ -57,17 +57,16 @@ public final class Workspace { 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. - * @param filter applies an optional filter to the outputs - * @param strict whether it should be a strict match (equals) or not (startsWith) + * Searches for buffers matching the filter in this workspace. + * @param filter the filter to apply * @return an array containing file tree as flat paths */ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public String[] filetree(Optional filter, boolean strict) { - return filetree(this.ptr, filter.orElse(null), strict); + public String[] searchBuffers(Optional filter) { + return search_buffers(this.ptr, filter.orElse(null)); } private static native String[] active_buffers(long self); @@ -125,37 +124,39 @@ public final class Workspace { 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 */ - public void fetchBuffers() throws ConnectionRemoteException { - fetch_buffers(this.ptr); + public String[] fetchBuffers() throws ConnectionRemoteException { + 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 */ - public void fetchUsers() throws ConnectionRemoteException { - fetch_buffers(this.ptr); + public User[] fetchUsers() throws ConnectionRemoteException { + 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. * @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 */ - public UUID[] listBufferUsers(String path) throws ConnectionRemoteException { - return list_buffer_users(this.ptr, path); + public User[] fetchBufferUsers(String path) throws ConnectionRemoteException { + return fetch_buffer_users(this.ptr, path); } private static native void delete_buffer(long self, String path) throws ConnectionRemoteException; diff --git a/dist/lua/annotations.lua b/dist/lua/annotations.lua index 9a411ba..16b8a4f 100644 --- a/dist/lua/annotations.lua +++ b/dist/lua/annotations.lua @@ -158,6 +158,17 @@ function MaybeBufferUpdatePromise:cancel() end ---invoke callback asynchronously as soon as promise is ready 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 ]] @@ -212,13 +223,17 @@ function Client:delete_workspace(ws) end ---grant user acccess to workspace 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 ---@async ---@nodiscard ----grant user acccess to workspace -function Client:list_workspaces(owned, invited) end +---fetch and list owned workspaces +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 ---@return Workspace? @@ -281,10 +296,9 @@ function Workspace:attach_buffer(path) end function Workspace:detach_buffer(path) end ---@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 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 all names of users currently in this workspace @@ -302,6 +316,13 @@ function Workspace:fetch_buffers(path) end ---force refresh users list from workspace 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 ---@field type string ---@field value string diff --git a/dist/py/src/codemp/codemp.pyi b/dist/py/src/codemp/codemp.pyi index 438bdbe..9d32002 100644 --- a/dist/py/src/codemp/codemp.pyi +++ b/dist/py/src/codemp/codemp.pyi @@ -51,7 +51,8 @@ class Client: def create_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 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 get_workspace(self, id: str) -> Workspace: ... def active_workspaces(self) -> list[str]: ... @@ -69,16 +70,16 @@ class Workspace: def create_buffer(self, path: str) -> Promise[None]: ... def attach_buffer(self, path: str) -> Promise[BufferController]: ... def detach_buffer(self, path: str) -> bool: ... - def fetch_buffers(self) -> Promise[None]: ... - def fetch_users(self) -> Promise[None]: ... - def list_buffer_users(self, path: str) -> Promise[list[str]]: ... + def fetch_buffers(self) -> Promise[list[str]]: ... + def fetch_users(self) -> Promise[list[User]]: ... + def fetch_buffer_users(self, path: str) -> Promise[list[User]]: ... def delete_buffer(self, path: str) -> Promise[None]: ... def id(self) -> str: ... def cursor(self) -> CursorController: ... def get_buffer(self, path: str) -> Optional[BufferController]: ... def user_list(self) -> list[User]: ... 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 try_recv(self) -> Promise[Optional[Event]]: ... def poll(self) -> Promise[None]: ... diff --git a/src/api/mod.rs b/src/api/mod.rs index 158a4d0..9e27c22 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -19,11 +19,9 @@ pub mod event; /// data structure for remote users pub mod user; -pub use change::BufferUpdate; -pub use change::TextChange; +pub use change::{BufferUpdate, TextChange}; pub use config::Config; -pub use controller::Controller; -pub use cursor::Cursor; -pub use cursor::Selection; +pub use controller::{AsyncReceiver, AsyncSender, Controller}; +pub use cursor::{Cursor, Selection}; pub use event::Event; pub use user::User; diff --git a/src/buffer/worker.rs b/src/buffer/worker.rs index 19b25d7..8b7bc72 100644 --- a/src/buffer/worker.rs +++ b/src/buffer/worker.rs @@ -170,8 +170,11 @@ impl BufferWorker { // in case we have a "replace" span if change.is_delete() { - self.branch - .delete_without_content(&mut self.oplog, self.agent_id, clip_start..clip_end); + self.branch.delete_without_content( + &mut self.oplog, + self.agent_id, + clip_start..clip_end, + ); } if change.is_insert() { @@ -247,7 +250,9 @@ impl BufferWorker { { tracing::warn!( "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 { diff --git a/src/client.rs b/src/client.rs index b7e7138..17ddcbb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -130,9 +130,18 @@ impl Client { Ok(()) } - /// List all available workspaces, also filtering between those owned and those invited to. - pub async fn list_workspaces(&self, owned: bool, invited: bool) -> RemoteResult> { - let mut workspaces = self + /// Fetch the names of all workspaces owned by the current user. + pub async fn fetch_owned_workspaces(&self) -> RemoteResult> { + self.fetch_workspaces(true).await + } + + /// Fetch the names of all workspaces the current user has joined. + pub async fn fetch_joined_workspaces(&self) -> RemoteResult> { + self.fetch_workspaces(false).await + } + + async fn fetch_workspaces(&self, owned: bool) -> RemoteResult> { + let workspaces = self .0 .session .clone() @@ -140,20 +149,18 @@ impl Client { .await? .into_inner(); - let mut out = Vec::new(); - 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`]. - pub async fn attach_workspace(&self, workspace: impl AsRef) -> ConnectionResult { + pub async fn attach_workspace( + &self, + workspace: impl AsRef, + ) -> ConnectionResult { let token = self .0 .session diff --git a/src/ffi/java/buffer.rs b/src/ffi/java/buffer.rs index 5f06f8e..edd7b9a 100644 --- a/src/ffi/java/buffer.rs +++ b/src/ffi/java/buffer.rs @@ -2,10 +2,7 @@ use jni::{objects::JObject, JNIEnv}; use jni_toolbox::jni; use crate::{ - api::{ - controller::{AsyncReceiver, AsyncSender}, - BufferUpdate, TextChange, - }, + api::{AsyncReceiver, AsyncSender, BufferUpdate, TextChange}, errors::ControllerError, }; diff --git a/src/ffi/java/client.rs b/src/ffi/java/client.rs index a65d883..230b26a 100644 --- a/src/ffi/java/client.rs +++ b/src/ffi/java/client.rs @@ -46,14 +46,16 @@ fn invite_to_workspace( super::tokio().block_on(client.invite_to_workspace(workspace, user)) } -/// List available workspaces. +/// List owned workspaces. #[jni(package = "mp.code", class = "Client")] -fn list_workspaces( - client: &mut Client, - owned: bool, - invited: bool, -) -> Result, RemoteError> { - super::tokio().block_on(client.list_workspaces(owned, invited)) +fn fetch_owned_workspaces(client: &mut Client) -> Result, RemoteError> { + super::tokio().block_on(client.fetch_owned_workspaces()) +} + +/// List joined workspaces. +#[jni(package = "mp.code", class = "Client")] +fn fetch_joined_workspaces(client: &mut Client) -> Result, RemoteError> { + super::tokio().block_on(client.fetch_joined_workspaces()) } /// List available workspaces. diff --git a/src/ffi/java/cursor.rs b/src/ffi/java/cursor.rs index e08a788..4457ddc 100644 --- a/src/ffi/java/cursor.rs +++ b/src/ffi/java/cursor.rs @@ -1,8 +1,5 @@ use crate::{ - api::{ - controller::{AsyncReceiver, AsyncSender}, - Cursor, Selection, - }, + api::{AsyncReceiver, AsyncSender, Cursor, Selection}, errors::ControllerError, }; use jni::{objects::JObject, JNIEnv}; diff --git a/src/ffi/java/workspace.rs b/src/ffi/java/workspace.rs index 4cca434..3548a92 100644 --- a/src/ffi/java/workspace.rs +++ b/src/ffi/java/workspace.rs @@ -25,10 +25,10 @@ fn get_buffer(workspace: &mut Workspace, path: String) -> Option, strict: bool) -> Vec { - workspace.filetree(filter.as_deref(), strict) +fn search_buffers(workspace: &mut Workspace, filter: Option) -> Vec { + workspace.search_buffers(filter.as_deref()) } /// 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. #[jni(package = "mp.code", class = "Workspace")] -fn fetch_buffers(workspace: &mut Workspace) -> Result<(), RemoteError> { +fn fetch_buffers(workspace: &mut Workspace) -> Result, RemoteError> { super::tokio().block_on(workspace.fetch_buffers()) } /// Update the local user list. #[jni(package = "mp.code", class = "Workspace")] -fn fetch_users(workspace: &mut Workspace) -> Result<(), RemoteError> { +fn fetch_users(workspace: &mut Workspace) -> Result, RemoteError> { 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")] -fn list_buffer_users( +fn fetch_buffer_users( workspace: &mut Workspace, path: String, ) -> Result, RemoteError> { - super::tokio().block_on(workspace.list_buffer_users(&path)) + super::tokio().block_on(workspace.fetch_buffer_users(&path)) } /// Delete a buffer. diff --git a/src/ffi/js/client.rs b/src/ffi/js/client.rs index fc92560..b6034f6 100644 --- a/src/ffi/js/client.rs +++ b/src/ffi/js/client.rs @@ -46,14 +46,16 @@ impl Client { Ok(self.delete_workspace(workspace).await?) } - #[napi(js_name = "listWorkspaces")] - /// list available workspaces - pub async fn js_list_workspaces( - &self, - owned: bool, - invited: bool, - ) -> napi::Result> { - Ok(self.list_workspaces(owned, invited).await?) + #[napi(js_name = "fetchOwnedWorkspaces")] + /// fetch owned workspaces + pub async fn js_fetch_owned_workspaces(&self) -> napi::Result> { + Ok(self.fetch_owned_workspaces().await?) + } + + #[napi(js_name = "fetchJoinedWorkspaces")] + /// fetch joined workspaces + pub async fn js_fetch_joined_workspaces(&self) -> napi::Result> { + Ok(self.fetch_joined_workspaces().await?) } #[napi(js_name = "inviteToWorkspace")] diff --git a/src/ffi/js/workspace.rs b/src/ffi/js/workspace.rs index c24658b..70b4c7d 100644 --- a/src/ffi/js/workspace.rs +++ b/src/ffi/js/workspace.rs @@ -44,9 +44,9 @@ impl Workspace { } /// List all available buffers in this workspace - #[napi(js_name = "filetree")] - pub fn js_filetree(&self, filter: Option<&str>, strict: bool) -> Vec { - self.filetree(filter, strict) + #[napi(js_name = "search_buffers")] + pub fn js_search_buffers(&self, filter: Option<&str>) -> Vec { + self.search_buffers(filter) } /// List all user names currently in this workspace @@ -121,7 +121,7 @@ impl 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 - // 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(()) @@ -137,23 +137,28 @@ impl Workspace { /// Re-fetch remote buffer list #[napi(js_name = "fetchBuffers")] - pub async fn js_fetch_buffers(&self) -> napi::Result<()> { + pub async fn js_fetch_buffers(&self) -> napi::Result> { Ok(self.fetch_buffers().await?) } /// Re-fetch the list of all users in the workspace. #[napi(js_name = "fetchUsers")] - pub async fn js_fetch_users(&self) -> napi::Result<()> { - Ok(self.fetch_users().await?) + pub async fn js_fetch_users(&self) -> napi::Result> { + Ok(self + .fetch_users() + .await? + .into_iter() + .map(JsUser::from) + .collect()) } /// List users attached to a specific buffer - #[napi(js_name = "listBufferUsers")] - pub async fn js_list_buffer_users( + #[napi(js_name = "fetchBufferUsers")] + pub async fn js_fetch_buffer_users( &self, path: String, ) -> napi::Result> { Ok(self - .list_buffer_users(&path) + .fetch_buffer_users(&path) .await? .into_iter() .map(super::client::JsUser::from) diff --git a/src/ffi/lua/client.rs b/src/ffi/lua/client.rs index cb54eff..4a8f4d2 100644 --- a/src/ffi/lua/client.rs +++ b/src/ffi/lua/client.rs @@ -12,8 +12,12 @@ impl LuaUserData for CodempClient { Ok(format!("{:?}", this)) }); - methods.add_method("current_user", |_, this, ()| Ok(this.current_user().clone())); - methods.add_method("active_workspaces", |_, this, ()| Ok(this.active_workspaces())); + methods.add_method("current_user", |_, this, ()| { + Ok(this.current_user().clone()) + }); + methods.add_method("active_workspaces", |_, this, ()| { + Ok(this.active_workspaces()) + }); methods.add_method( "refresh", @@ -39,8 +43,14 @@ impl LuaUserData for CodempClient { a_sync! { this => this.invite_to_workspace(ws, user).await? } ); - methods.add_method("list_workspaces", |_, this, (owned,invited):(Option,Option)| - a_sync! { this => this.list_workspaces(owned.unwrap_or(true), invited.unwrap_or(true)).await? } + methods.add_method( + "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,)| { diff --git a/src/ffi/lua/ext/callback.rs b/src/ffi/lua/ext/callback.rs index 7c5f360..54d8e03 100644 --- a/src/ffi/lua/ext/callback.rs +++ b/src/ffi/lua/ext/callback.rs @@ -101,6 +101,7 @@ macro_rules! callback_args { callback_args! { Str: String, VecStr: Vec, + VecUser: Vec, Client: CodempClient, CursorController: CodempCursorController, BufferController: CodempBufferController, diff --git a/src/ffi/lua/workspace.rs b/src/ffi/lua/workspace.rs index dd80f05..2094f75 100644 --- a/src/ffi/lua/workspace.rs +++ b/src/ffi/lua/workspace.rs @@ -43,12 +43,15 @@ impl LuaUserData for CodempWorkspace { |_, this, ()| a_sync! { this => this.fetch_users().await? }, ); - methods.add_method( - "filetree", - |_, this, (filter, strict): (Option, Option)| { - Ok(this.filetree(filter.as_deref(), strict.unwrap_or(false))) - }, - ); + methods.add_method("search_buffers", |_, this, (filter,): (Option,)| { + Ok(this.search_buffers(filter.as_deref())) + }); + + 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("cursor", |_, this, ()| Ok(this.cursor())); diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 1ddd89a..686045b 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -5,26 +5,26 @@ //! ```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::new( //! "mail@example.net", "dont-use-this-password" //! )).await?; -//! +//! //! // create and join a workspace //! client.create_workspace("some-workspace").await?; //! let workspace = client.join_workspace("some-workspace").await?; -//! +//! //! // create a new buffer in this workspace and attach to it //! workspace.create("/my/file.txt").await?; //! let buffer = workspace.attach("/my/file.txt").await?; -//! +//! //! // write `hello!` at the beginning of this buffer //! buffer.send(codemp::api::TextChange { //! start: 0, end: 0, //! content: "hello!".to_string(), //! })?; -//! +//! //! // wait for cursor movements //! loop { //! let event = workspace.cursor().recv().await?; @@ -42,26 +42,26 @@ //! //! ```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.create_workspace("some-workspace"); //! let workspace = await client.join_workspace("some-workspace"); -//! +//! //! // create a new buffer in this workspace and attach to it //! await workspace.create("/my/file.txt"); //! let buffer = await workspace.attach("/my/file.txt"); -//! +//! //! // write `hello!` at the beginning of this buffer //! await buffer.send({ //! start: 0, end: 0, //! content: "hello!", //! }); -//! +//! //! // wait for cursor movements //! while (true) { //! let event = await workspace.cursor().recv(); @@ -78,26 +78,26 @@ //! //! ```py //! import codemp -//! +//! //! # connect first, api.code.mp is managed by hexed.technology //! config = codemp.get_default_config() //! config.username = "mail@example.net" //! config.password = "dont-use-this-password" //! client = codemp.connect(config).wait() -//! +//! //! # create and join a workspace //! client.create_workspace("some-workspace").wait() //! workspace = client.join_workspace("some-workspace").wait() -//! +//! //! # create a new buffer in this workspace and attach to it //! workspace.create("/my/file.txt").wait() //! buffer = workspace.attach("/my/file.txt").wait() -//! +//! //! # write `hello!` at the beginning of this buffer //! buffer.send( //! 0, 0, "hello!" //! ).wait() -//! +//! //! # wait for cursor movements //! while true: //! event = workspace.cursor().recv().wait() @@ -124,26 +124,26 @@ //! //! ```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:join_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 = 0, finish = 0, +//! start = 0, finish = 0, //! content = "hello!" //! }):await() -//! +//! //! -- wait for cursor movements //! while true do //! local event = workspace.cursor:recv():await() @@ -162,26 +162,26 @@ //! //! ```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.joinWorkspace("some-workspace"); -//! +//! //! // create a new buffer in this workspace and attach to it //! workspace.createBuffer("/my/file.txt"); //! BufferController buffer = workspace.attachToBuffer("/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.getCursor().recv(); diff --git a/src/ffi/python/client.rs b/src/ffi/python/client.rs index 7d63406..46bc033 100644 --- a/src/ffi/python/client.rs +++ b/src/ffi/python/client.rs @@ -55,16 +55,18 @@ impl Client { a_sync_allow_threads!(py, this.invite_to_workspace(workspace, user).await) } - #[pyo3(name = "list_workspaces")] - fn pylist_workspaces( - &self, - py: Python<'_>, - owned: bool, - invited: bool, - ) -> PyResult { - tracing::info!("attempting to list workspaces"); + #[pyo3(name = "fetch_owned_workspaces")] + fn pyfetch_owned_workspaces(&self, py: Python<'_>) -> PyResult { + tracing::info!("attempting to fetch owned workspaces"); 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 { + 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")] diff --git a/src/ffi/python/workspace.rs b/src/ffi/python/workspace.rs index ed914f5..8d3c525 100644 --- a/src/ffi/python/workspace.rs +++ b/src/ffi/python/workspace.rs @@ -41,11 +41,11 @@ impl Workspace { a_sync_allow_threads!(py, this.fetch_users().await) } - #[pyo3(name = "list_buffer_users")] - fn pylist_buffer_users(&self, py: Python, path: String) -> PyResult { + #[pyo3(name = "fetch_buffer_users")] + fn pyfetch_buffer_users(&self, py: Python, path: String) -> PyResult { // crate::Result> 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")] @@ -74,10 +74,10 @@ impl Workspace { self.active_buffers() } - #[pyo3(name = "filetree")] - #[pyo3(signature = (filter=None, strict=false))] - fn pyfiletree(&self, filter: Option<&str>, strict: bool) -> Vec { - self.filetree(filter, strict) + #[pyo3(name = "search_buffers")] + #[pyo3(signature = (filter=None))] + fn pysearch_buffers(&self, filter: Option<&str>) -> Vec { + self.search_buffers(filter) } #[pyo3(name = "user_list")] diff --git a/src/prelude.rs b/src/prelude.rs index 71bb813..5f96584 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -2,7 +2,7 @@ //! All-in-one renamed imports with `use codemp::prelude::*`. 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, Cursor as CodempCursor, Event as CodempEvent, Selection as CodempSelection, TextChange as CodempTextChange, User as CodempUser, diff --git a/src/workspace.rs b/src/workspace.rs index 8d04910..f8054fc 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -26,7 +26,7 @@ use codemp_proto::{ }; use dashmap::{DashMap, DashSet}; -use std::{collections::BTreeSet, sync::Arc}; +use std::sync::Arc; use tokio::sync::{mpsc, mpsc::error::TryRecvError}; use tonic::Streaming; use uuid::Uuid; @@ -201,45 +201,48 @@ impl 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> { let mut workspace_client = self.0.services.ws(); - let buffers = workspace_client + let resp = workspace_client .list_buffers(tonic::Request::new(Empty {})) .await? - .into_inner() - .buffers; + .into_inner(); + + let mut out = Vec::new(); self.0.filetree.clear(); - for b in buffers { - self.0.filetree.insert(b.path); + for b in resp.buffers { + self.0.filetree.insert(b.path.clone()); + out.push(b.path); } - Ok(()) + Ok(out) } /// Re-fetch the list of all users in the workspace. - pub async fn fetch_users(&self) -> RemoteResult<()> { + pub async fn fetch_users(&self) -> RemoteResult> { let mut workspace_client = self.0.services.ws(); - let users = BTreeSet::from_iter( - workspace_client - .list_users(tonic::Request::new(Empty {})) - .await? - .into_inner() - .users - .into_iter() - .map(User::from), - ); + let users = workspace_client + .list_users(tonic::Request::new(Empty {})) + .await? + .into_inner() + .users + .into_iter() + .map(User::from); + + let mut result = Vec::new(); self.0.users.clear(); 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. - pub async fn list_buffer_users(&self, path: &str) -> RemoteResult> { + /// Fetch a list of the [User]s attached to a specific buffer. + pub async fn fetch_buffer_users(&self, path: &str) -> RemoteResult> { let mut workspace_client = self.0.services.ws(); let buffer_users = workspace_client .list_buffer_users(tonic::Request::new(BufferNode { @@ -311,20 +314,12 @@ impl Workspace { /// 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). // #[cfg_attr(feature = "js", napi)] // https://github.com/napi-rs/napi-rs/issues/1120 - pub fn filetree(&self, filter: Option<&str>, strict: bool) -> Vec { + pub fn search_buffers(&self, filter: Option<&str>) -> Vec { let mut tree = self .0 .filetree .iter() - .filter(|f| { - filter.map_or(true, |flt| { - if strict { - f.as_str() == flt - } else { - f.starts_with(flt) - } - }) - }) + .filter(|f| filter.map_or(true, |flt| f.starts_with(flt))) .map(|f| f.clone()) .collect::>(); tree.sort();