mirror of
https://github.com/hexedtech/codemp-nvim.git
synced 2024-11-22 15:34:53 +01:00
257 lines
8.1 KiB
Lua
257 lines
8.1 KiB
Lua
local utils = require('codemp.utils')
|
|
local buffers = require('codemp.buffers')
|
|
|
|
---@class UserHighlight
|
|
---@field ns integer namespace to use for this user
|
|
---@field hi HighlightPair color for user to use
|
|
---@field mark integer | nil extmark id
|
|
---@field pos [integer, integer] cursor start position of this user
|
|
|
|
---@type table<string, UserHighlight>
|
|
local user_hl = {}
|
|
|
|
local function fetch_workspaces_list()
|
|
local new_list = {}
|
|
CODEMP.client:fetch_owned_workspaces():and_then(function (owned)
|
|
for _, ws in pairs(owned) do
|
|
table.insert(new_list, {
|
|
name = ws,
|
|
owned = true,
|
|
})
|
|
end
|
|
CODEMP.client:fetch_joined_workspaces():and_then(function (invited)
|
|
for _, ws in pairs(invited) do
|
|
table.insert(new_list, {
|
|
name = ws,
|
|
owned = false,
|
|
})
|
|
end
|
|
CODEMP.available = new_list
|
|
require('codemp.window').update()
|
|
end)
|
|
end)
|
|
end
|
|
|
|
local last_jump = { 0, 0 }
|
|
local workspace_callback_group = nil
|
|
|
|
---@param controller CursorController
|
|
---@param name string
|
|
local function register_cursor_callback(controller, name)
|
|
local once = true
|
|
workspace_callback_group = vim.api.nvim_create_augroup("codemp-workspace-" .. name, { clear = true })
|
|
vim.api.nvim_create_autocmd({"CursorMoved", "CursorMovedI", "ModeChanged"}, {
|
|
group = workspace_callback_group,
|
|
callback = function (_ev)
|
|
if CODEMP.ignore_following_action then
|
|
CODEMP.ignore_following_action = false
|
|
return
|
|
elseif CODEMP.following ~= nil then
|
|
print(" / / unfollowing " .. CODEMP.following)
|
|
CODEMP.following = nil
|
|
require('codemp.window').update()
|
|
end
|
|
local cur = utils.cursor.position()
|
|
local buf = vim.api.nvim_get_current_buf()
|
|
local bufname
|
|
if buffers.map[buf] ~= nil then
|
|
bufname = buffers.map[buf]
|
|
once = true
|
|
local _ = controller:send({
|
|
buffer = bufname,
|
|
start_row = cur[1][1],
|
|
start_col = cur[1][2],
|
|
end_row = cur[2][1],
|
|
end_col = cur[2][2],
|
|
})
|
|
else -- set ourselves "away" only once
|
|
bufname = ""
|
|
if once then
|
|
controller:send({
|
|
buffer = bufname,
|
|
start_row = 0,
|
|
start_col = 0,
|
|
end_row = 1,
|
|
end_col = 0,
|
|
})
|
|
end
|
|
once = false
|
|
end
|
|
local oldbuf = buffers.users[CODEMP.client:current_user().name]
|
|
buffers.users[CODEMP.client:current_user().name] = bufname
|
|
if oldbuf ~= bufname then
|
|
require('codemp.window').update()
|
|
end
|
|
end
|
|
})
|
|
end
|
|
|
|
---@param controller CursorController
|
|
local function register_cursor_handler(controller)
|
|
local async = vim.loop.new_async(vim.schedule_wrap(function ()
|
|
while true do
|
|
local event = controller:try_recv():await()
|
|
if event == nil then break end
|
|
local user = event.user -- do it on separate line so language server understands that it wont be nil
|
|
if user ~= nil then
|
|
if user_hl[user] == nil then
|
|
user_hl[user] = {
|
|
ns = vim.api.nvim_create_namespace("codemp-cursor-" .. event.user),
|
|
hi = utils.color(event.user),
|
|
mark = nil,
|
|
pos = { 0, 0 },
|
|
}
|
|
end
|
|
user_hl[user].pos = { event.sel.start_row, event.sel.start_col }
|
|
local old_buffer = buffers.users[event.user]
|
|
if old_buffer ~= nil then
|
|
local old_buffer_id = buffers.map_rev[old_buffer]
|
|
if old_buffer_id ~= nil then
|
|
vim.api.nvim_buf_clear_namespace(old_buffer_id, user_hl[event.user].ns, 0, -1)
|
|
end
|
|
end
|
|
buffers.users[event.user] = event.sel.buffer
|
|
local buffer_id = buffers.map_rev[event.sel.buffer]
|
|
if buffer_id ~= nil then
|
|
local hi = user_hl[event.user].hi
|
|
local event_finish_2 = event.sel.end_col -- TODO can't set the tuple field? need to copy out
|
|
if event.sel.start_row == event.sel.end_row and event.sel.start_col == event.sel.end_col then
|
|
-- vim can't draw 0-width cursors, so we always expand them to at least 1 width
|
|
event_finish_2 = event.sel.end_col + 1
|
|
end
|
|
user_hl[event.user].mark = vim.api.nvim_buf_set_extmark(
|
|
buffer_id,
|
|
user_hl[event.user].ns,
|
|
event.sel.start_row,
|
|
event.sel.start_col,
|
|
{
|
|
id = user_hl[event.user].mark,
|
|
end_row = event.sel.end_row,
|
|
end_col = event_finish_2,
|
|
hl_group = hi.bg,
|
|
virt_text_pos = "right_align",
|
|
sign_text = string.sub(event.user, 0, 1),
|
|
sign_hl_group = hi.bg,
|
|
virt_text_repeat_linebreak = true,
|
|
priority = 1000,
|
|
strict = false,
|
|
virt_text = {
|
|
{ " " .. event.user .. " ", hi.fg },
|
|
{ " ", hi.bg },
|
|
},
|
|
}
|
|
)
|
|
end
|
|
if old_buffer ~= event.sel.buffer then
|
|
require('codemp.window').update() -- redraw user positions
|
|
end
|
|
if CODEMP.following ~= nil and CODEMP.following == event.user then
|
|
local buf_id = buffers.map_rev[event.sel.buffer]
|
|
if buf_id ~= nil then
|
|
local win = vim.api.nvim_get_current_win()
|
|
local curr_buf = vim.api.nvim_get_current_buf()
|
|
CODEMP.ignore_following_action = true
|
|
if curr_buf ~= buf_id then
|
|
vim.api.nvim_win_set_buf(win, buf_id)
|
|
end
|
|
-- keep centered the cursor end that is currently being moved, but prefer start
|
|
if event.sel.start_row == last_jump[1] and event.sel.start_col == last_jump[2] then
|
|
vim.api.nvim_win_set_cursor(win, { event.sel.end_row + 1, event.sel.end_col })
|
|
else
|
|
vim.api.nvim_win_set_cursor(win, { event.sel.start_row + 1, event.sel.start_col })
|
|
end
|
|
last_jump = { event.sel.start_row, event.sel.start_col }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end))
|
|
controller:callback(function (_controller) async:send() end)
|
|
end
|
|
|
|
local events_poller = nil
|
|
|
|
---@param workspace string workspace name to join
|
|
---join a workspace and register event handlers
|
|
local function join(workspace)
|
|
print(" <> joining workspace " .. workspace .. " ...")
|
|
CODEMP.client:attach_workspace(workspace):and_then(function (ws)
|
|
print(" >< joined workspace " .. ws:id())
|
|
register_cursor_callback(ws:cursor(), ws:id())
|
|
register_cursor_handler(ws:cursor())
|
|
CODEMP.workspace = ws
|
|
for _, user in pairs(CODEMP.workspace:user_list()) do
|
|
buffers.users[user.name] = ""
|
|
user_hl[user.name] = {
|
|
ns = vim.api.nvim_create_namespace("codemp-cursor-" .. user.name),
|
|
hi = utils.color(user.name),
|
|
pos = { 0, 0 },
|
|
mark = nil,
|
|
}
|
|
end
|
|
require('codemp.window').update()
|
|
local async = vim.uv.new_async(function ()
|
|
while true do
|
|
local event = ws:try_recv():await()
|
|
if event == nil then break end
|
|
if event.type == "leave" then
|
|
if buffers.users[event.value] ~= nil then
|
|
local buf_name = buffers.users[event.value]
|
|
local buf_id = buffers.map_rev[buf_name]
|
|
if buf_id ~= nil then
|
|
vim.api.nvim_buf_clear_namespace(buf_id, user_hl[event.value].ns, 0, -1)
|
|
end
|
|
buffers.users[event.value] = nil
|
|
user_hl[event.value] = nil
|
|
end
|
|
elseif event.type == "join" then
|
|
buffers.users[event.value] = ""
|
|
user_hl[event.value] = {
|
|
ns = vim.api.nvim_create_namespace("codemp-cursor-" .. event.value),
|
|
hi = utils.color(event.value),
|
|
pos = { 0, 0 },
|
|
mark = nil,
|
|
}
|
|
end
|
|
end
|
|
require('codemp.window').update()
|
|
end)
|
|
ws:callback(function(_) async:send() end)
|
|
end)
|
|
end
|
|
|
|
local function leave()
|
|
local ws_name = CODEMP.workspace:id()
|
|
CODEMP.workspace:cursor():clear_callback()
|
|
vim.api.nvim_clear_autocmds({ group = workspace_callback_group })
|
|
for id, name in pairs(buffers.map) do
|
|
CODEMP.workspace:get_buffer(name):clear_callback()
|
|
buffers.map[id] = nil
|
|
buffers.map_rev[name] = nil
|
|
end
|
|
for user, _buf in pairs(buffers.users) do
|
|
buffers.users[user] = nil
|
|
end
|
|
CODEMP.workspace = nil
|
|
if events_poller ~= nil then
|
|
events_poller:stop()
|
|
events_poller = nil
|
|
end
|
|
if not CODEMP.client:leave_workspace(ws_name) then
|
|
collectgarbage("collect")
|
|
-- TODO codemp disconnects when all references to its objects are dropped. since it
|
|
-- hands out Arc<> of things, all references still not garbage collected in Lua will
|
|
-- prevent it from disconnecting. while running a full cycle may be a bit slow, this
|
|
-- only happens when manually requested, and it's not like the extra garbage collection
|
|
-- is an effort for nothing... still it would be more elegant to not need this!!
|
|
end
|
|
print(" -- left workspace " .. ws_name)
|
|
require('codemp.window').update()
|
|
end
|
|
|
|
return {
|
|
join = join,
|
|
leave = leave,
|
|
map = user_hl,
|
|
list = fetch_workspaces_list,
|
|
}
|