feat(lua): type annotations

This commit is contained in:
əlemi 2024-08-22 20:21:43 +02:00
parent 30f692e6e1
commit e1da62f0c8
Signed by: alemi
GPG key ID: A4895B84D311642C
2 changed files with 321 additions and 5 deletions

316
dist/lua/annotations.lua vendored Normal file
View file

@ -0,0 +1,316 @@
---@meta annotations
-- type annotations for codemp native lua library
-- [[ ASYNC STUFF ]]
-- TODO lua-language-server doesn't seem to support generic classes
-- https://github.com/LuaLS/lua-language-server/issues/1532
-- so we need to expand every possible promise type...
---@class Promise
---@field ready boolean true if promise completed
---@class NilPromise : Promise
---@field await fun(self: NilPromise): nil block until promise is ready
---@class StringArrayPromise : Promise
---@field await fun(self: StringArrayPromise): string[] block until promise is ready and return value
---@class ClientPromise : Promise
---@field await fun(self: ClientPromise): Client block until promise is ready and return value
---@class WorkspacePromise : Promise
---@field await fun(self: WorkspacePromise): Workspace block until promise is ready and return value
---@class WorkspaceEventPromise : Promise
---@field await fun(self: WorkspaceEventPromise): WorkspaceEvent block until promise is ready and return value
---@class BufferControllerPromise : Promise
---@field await fun(self: BufferControllerPromise): BufferController block until promise is ready and return value
---@class CursorPromise : Promise
---@field await fun(self: CursorPromise): Cursor block until promise is ready and return value
---@class MaybeCursorPromise : Promise
---@field await fun(self: MaybeCursorPromise): Cursor? block until promise is ready and return value
---@class TextChangePromise : Promise
---@field await fun(self: TextChangePromise): TextChange block until promise is ready and return value
---@class MaybeTextChangePromise : Promise
---@field await fun(self: MaybeTextChangePromise): TextChange? block until promise is ready and return value
-- [[ END ASYNC STUFF ]]
---@class Client
---@field id string uuid of local user
---@field username string name of local user
---@field active_workspaces string[] array of all currently active workspace names
---the effective local client, handling connecting to codemp server
local Client = {}
---@return NilPromise
---@async
---@nodiscard
---refresh current user token if possible
function Client:refresh() end
---@param ws string workspace id to connect to
---@return WorkspacePromise
---@async
---@nodiscard
---join requested workspace if possible and subscribe to event bus
function Client:join_workspace(ws) end
---@param ws string workspace id to create
---@return NilPromise
---@async
---@nodiscard
---create a new workspace with given id
function Client:join_workspace(ws) end
---@param ws string workspace id to leave
---leave workspace with given id, detaching and disconnecting
function Client:leave_workspace(ws) end
---@param ws string workspace id to delete
---@return NilPromise
---@async
---@nodiscard
---delete workspace with given id
function Client:delete_workspace(ws) end
---@param ws string workspace id to delete
---@param user string user name to invite to given workspace
---@return NilPromise
---@async
---@nodiscard
---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
---@param ws string workspace id to get
---@return Workspace?
---get an active workspace by name
function Client:get_workspace(ws) end
---@class Workspace
---@field name string workspace name
---@field cursor CursorController workspace cursor controller
---@field active_buffers string[] array of all currently active buffer names
---a joined codemp workspace
local Workspace = {}
---@param path string relative path ("name") of new buffer
---@return NilPromise
---@async
---@nodiscard
---create a new empty buffer
function Workspace:create_buffer(path) end
---@param path string relative path ("name") of buffer to delete
---@return NilPromise
---@async
---@nodiscard
---delete buffer from workspace
function Workspace:delete_buffer(path) end
---@param path string relative path ("name") of buffer to get
---@return BufferController?
---get an active buffer controller by name
function Workspace:get_buffer(path) end
---@param path string relative path ("name") of buffer to attach to
---@return BufferControllerPromise
---@async
---@nodiscard
---attach to a remote buffer, synching content and changes and returning its controller
function Workspace:attach_buffer(path) end
---@param path string relative path ("name") of buffer to detach from
---@return boolean
---detach from an active buffer, closing all streams. returns false if buffer was no longer active
function Workspace:detach_buffer(path) end
---@param filter string only return elements starting with given filter
---@return string[]
---return the list of available buffers in this workspace, as relative paths from workspace root
function Workspace:filetree(filter) end
---@return NilPromise
---@async
---@nodiscard
---force refresh buffer list from workspace
function Workspace:fetch_buffers(path) end
---@return NilPromise
---@async
---@nodiscard
---force refresh users list from workspace
function Workspace:fetch_users(path) end
---@class WorkspaceEvent
---@field type string
---@field value string
---@return WorkspaceEventPromise
---@async
---@nodiscard
---get next workspace event
function Workspace:event() end
---@class BufferController
---handle to a remote buffer, for async send/recv operations
local BufferController = {}
---@class TextChange
---@field content string text content of change
---@field first integer start index of change
---@field last integer end index of change
---@field hash integer? optional hash of text buffer after this change, for sync checks
---@field apply fun(self: TextChange, other: string): string apply this text change to a string
---@param first integer change start index
---@param last integer change end index
---@param content string change content
---@return NilPromise
---@async
---@nodiscard
---update buffer with a text change; note that to delete content should be empty but not span, while to insert span should be empty but not content (can insert and delete at the same time)
function BufferController:send(first, last, content) end
---@return MaybeTextChangePromise
---@async
---@nodiscard
---try to receive text changes, returning nil if none is available
function BufferController:try_recv() end
---@return TextChangePromise
---@async
---@nodiscard
---block until next text change and return it
function BufferController:recv() end
---@return NilPromise
---@async
---@nodiscard
---block until next text change without returning it
function BufferController:poll() end
---@return boolean
---stop buffer worker and disconnect, returns false if was already stopped
function BufferController:stop() end
---clears any previously registered buffer callback
function BufferController:clear_callback() end
---@param cb fun(c: BufferController) callback to invoke on each text change from server
---register a new callback to be called on remote text changes (replaces any previously registered one)
function BufferController:callback(cb) end
---@class CursorController
---handle to a workspace's cursor channel, allowing send/recv operations
local CursorController = {}
---@class RowCol
---@field row integer row number
---@field col integer column number
---row and column tuple
---@class Cursor
---@field user string? id of user owning this cursor
---@field buffer string relative path ("name") of buffer on which this cursor is
---@field start RowCol cursor start position
---@field finish RowCol cursor end position
---a cursor position
---@param buffer string buffer relative path ("name") to send this cursor on
---@param start_row integer cursor start row
---@param start_col integer cursor start col
---@param end_row integer cursor end row
---@param end_col integer cursor end col
---@return NilPromise
---@async
---@nodiscard
---update cursor position by sending a cursor event to server
function CursorController:send(buffer, start_row, start_col, end_row, end_col) end
---@return MaybeCursorPromise
---@async
---@nodiscard
---try to receive cursor events, returning nil if none is available
function CursorController:try_recv() end
---@return CursorPromise
---@async
---@nodiscard
---block until next cursor event and return it
function CursorController:recv() end
---@return NilPromise
---@async
---@nodiscard
---block until next cursor event without returning it
function CursorController:poll() end
---@return boolean
---stop cursor worker and disconnect, returns false if was already stopped
function CursorController:stop() end
---clears any previously registered cursor callback
function CursorController:clear_callback() end
---@param cb fun(c: CursorController) callback to invoke on each cursor event from server
---register a new callback to be called on cursor events (replaces any previously registered one)
function CursorController:callback(cb) end
---@class Codemp
---the codemp shared library
local Codemp = {}
---@param host string server host to connect to
---@param username string username used to log in (usually email)
---@param password string password used to log in
---@return ClientPromise
---@async
---@nodiscard
---connect to codemp server, authenticate and return client
function Codemp.connect(host, username, password) end
---@param data string
---@return integer
---use xxh3 hash, returns an i64 from any string
function Codemp.hash(data) end
---@class RuntimeDriver
---@field stop fun(): boolean stops the runtime thread without deleting the runtime itself, returns false if driver was already stopped
---@return RuntimeDriver
---spawns a background thread and uses it to run the codemp runtime
function Codemp.spawn_runtime_driver() end
---@param printer string | fun(string) log sink used for printing, if string will go to file, otherwise use given function
---@param debug boolean? show more verbose debug logs, default false
---@return boolean true if logger was setup correctly, false otherwise
---setup a global logger for codemp, note that can only be done once
function Codemp.logger(printer, debug) end

View file

@ -77,7 +77,7 @@ macro_rules! a_sync {
}; };
} }
fn runtime_drive_forever(_: &Lua, ():()) -> LuaResult<Driver> { fn spawn_runtime_driver(_: &Lua, ():()) -> LuaResult<Driver> {
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
std::thread::spawn(move || tokio().block_on(async move { std::thread::spawn(move || tokio().block_on(async move {
tracing::info!(" :: driving runtime..."); tracing::info!(" :: driving runtime...");
@ -103,6 +103,7 @@ impl LuaUserData for CodempClient {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, this| Ok(this.user().id.to_string())); fields.add_field_method_get("id", |_, this| Ok(this.user().id.to_string()));
fields.add_field_method_get("username", |_, this| Ok(this.user().name.clone())); fields.add_field_method_get("username", |_, this| Ok(this.user().name.clone()));
fields.add_field_method_get("active_workspaces", |_, this| Ok(this.active_workspaces()));
} }
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
@ -137,7 +138,6 @@ impl LuaUserData for CodempClient {
); );
methods.add_method("get_workspace", |_, this, (ws,):(String,)| Ok(this.get_workspace(&ws))); methods.add_method("get_workspace", |_, this, (ws,):(String,)| Ok(this.get_workspace(&ws)));
methods.add_method("active_workspaces", |_, this, ()| Ok(this.active_workspaces()));
} }
} }
@ -192,7 +192,7 @@ impl LuaUserData for CodempWorkspace {
} }
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, this| Ok(this.id())); fields.add_field_method_get("name", |_, this| Ok(this.id()));
fields.add_field_method_get("cursor", |_, this| Ok(this.cursor())); fields.add_field_method_get("cursor", |_, this| Ok(this.cursor()));
fields.add_field_method_get("active_buffers", |_, this| Ok(this.buffer_list())); fields.add_field_method_get("active_buffers", |_, this| Ok(this.buffer_list()));
// fields.add_field_method_get("users", |_, this| Ok(this.0.users())); // TODO // fields.add_field_method_get("users", |_, this| Ok(this.0.users())); // TODO
@ -334,7 +334,7 @@ impl LuaUserData for CodempTextChange {
// define module and exports // define module and exports
#[mlua::lua_module] #[mlua::lua_module]
fn codemp_lua(lua: &Lua) -> LuaResult<LuaTable> { fn codemp_native(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?; let exports = lua.create_table()?;
// entrypoint // entrypoint
@ -348,7 +348,7 @@ fn codemp_lua(lua: &Lua) -> LuaResult<LuaTable> {
)?)?; )?)?;
// runtime // runtime
exports.set("runtime_drive_forever", lua.create_function(runtime_drive_forever)?)?; exports.set("spawn_runtime_driver", lua.create_function(spawn_runtime_driver)?)?;
// logging // logging
exports.set("logger", lua.create_function(logger)?)?; exports.set("logger", lua.create_function(logger)?)?;