135 lines
3 KiB
VimL
135 lines
3 KiB
VimL
vim9script
|
|
|
|
# collab.vim - collaborative editing adapter for collabd
|
|
# requires: bun, collabd daemon running
|
|
|
|
var bridge_job: job = null_job
|
|
var bridge_channel: channel = null_channel
|
|
var connected = false
|
|
var ready = false
|
|
var room = ""
|
|
var suppressing = false
|
|
|
|
# path to bridge script (adjust as needed)
|
|
const bridge_script = expand('<sfile>:p:h') .. '/bridge.ts'
|
|
|
|
def Send(msg: dict<any>)
|
|
if bridge_channel != null_channel
|
|
ch_sendraw(bridge_channel, json_encode(msg) .. "\n")
|
|
endif
|
|
enddef
|
|
|
|
def OnOutput(ch: channel, msg: string)
|
|
if empty(msg)
|
|
return
|
|
endif
|
|
var data: dict<any>
|
|
try
|
|
data = json_decode(msg)
|
|
catch
|
|
return
|
|
endtry
|
|
|
|
if data.type == 'ready'
|
|
ready = true
|
|
elseif data.type == 'connected'
|
|
connected = true
|
|
echom '[collab] connected to room: ' .. data.room
|
|
elseif data.type == 'disconnected'
|
|
connected = false
|
|
echom '[collab] disconnected'
|
|
elseif data.type == 'content'
|
|
ApplyRemoteContent(data.text)
|
|
elseif data.type == 'peers'
|
|
echom '[collab] peers: ' .. data.count
|
|
elseif data.type == 'error'
|
|
echoerr '[collab] ' .. data.message
|
|
endif
|
|
enddef
|
|
|
|
def ApplyRemoteContent(content: string)
|
|
if suppressing
|
|
return
|
|
endif
|
|
suppressing = true
|
|
var lines = split(content, "\n", true)
|
|
var view = winsaveview()
|
|
silent! :%delete _
|
|
setline(1, lines)
|
|
winrestview(view)
|
|
suppressing = false
|
|
enddef
|
|
|
|
def SendBuffer()
|
|
if !connected || suppressing
|
|
return
|
|
endif
|
|
var lines = getline(1, '$')
|
|
var content = join(lines, "\n")
|
|
Send({type: 'content', text: content})
|
|
enddef
|
|
|
|
export def Connect(room_name: string)
|
|
if bridge_job != null_job
|
|
Disconnect()
|
|
endif
|
|
|
|
room = room_name
|
|
ready = false
|
|
bridge_job = job_start(['bun', 'run', bridge_script], {
|
|
mode: 'nl',
|
|
out_cb: OnOutput,
|
|
err_io: 'null'
|
|
})
|
|
bridge_channel = job_getchannel(bridge_job)
|
|
|
|
# wait for bridge ready signal
|
|
var timeout = 50
|
|
while !ready && timeout > 0
|
|
sleep 10m
|
|
timeout -= 1
|
|
endwhile
|
|
|
|
if !ready
|
|
echoerr '[collab] bridge failed to start'
|
|
Disconnect()
|
|
return
|
|
endif
|
|
|
|
Send({type: 'connect', room: room_name})
|
|
|
|
# set up autocmds to send changes
|
|
augroup CollabVim
|
|
autocmd!
|
|
autocmd TextChanged,TextChangedI * call SendBuffer()
|
|
augroup END
|
|
enddef
|
|
|
|
export def Disconnect()
|
|
if bridge_job != null_job
|
|
Send({type: 'disconnect'})
|
|
job_stop(bridge_job)
|
|
bridge_job = null_job
|
|
bridge_channel = null_channel
|
|
endif
|
|
connected = false
|
|
ready = false
|
|
room = ""
|
|
augroup CollabVim
|
|
autocmd!
|
|
augroup END
|
|
echom '[collab] disconnected'
|
|
enddef
|
|
|
|
export def Status()
|
|
if connected
|
|
echom '[collab] connected to room: ' .. room
|
|
else
|
|
echom '[collab] not connected'
|
|
endif
|
|
enddef
|
|
|
|
# commands
|
|
command! -nargs=1 CollabJoin call Connect(<q-args>)
|
|
command! CollabLeave call Disconnect()
|
|
command! CollabStatus call Status()
|