vim9script # collab.vim - collaborative editing adapter for collabd # requires: bun, collabd daemon running var bridge_job: job var bridge_channel: channel var connected = false var ready = false var room = "" var suppressing = false # path to bridge script (adjust as needed) const bridge_script = expand(':p:h') .. '/bridge.ts' def Send(msg: dict) if bridge_channel != null ch_sendraw(bridge_channel, json_encode(msg) .. "\n") endif enddef def OnOutput(ch: channel, msg: string) if empty(msg) return endif var data: dict 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 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 Send({type: 'disconnect'}) job_stop(bridge_job) bridge_job = null bridge_channel = null 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() command! CollabLeave call Disconnect() command! CollabStatus call Status()