diff --git a/lua/deps/msgpack.lua b/lua/deps/msgpack.lua index a4b928b..d7ccfdd 100644 --- a/lua/deps/msgpack.lua +++ b/lua/deps/msgpack.lua @@ -9,120 +9,140 @@ local double_encode_count = 0 -- cache bitops local band, rshift = luabit.band, luabit.brshift if not rshift then -- luajit differ from luabit - rshift = luabit.rshift + rshift = luabit.rshift end -local function byte_mod(x,v) - if x < 0 then - x = x + 256 - end - return (x%v) +local function byte_mod(x, v) + if x < 0 then + x = x + 256 + end + return (x % v) end - -- buffer local strbuf = "" -- for unpacking local strary = {} -- for packing -local function strary_append_int16(n,h) - if n < 0 then - n = n + 65536 - end - table.insert( strary, tostr(h, math.floor(n / 256), n % 256 ) ) +local function strary_append_int16(n, h) + if n < 0 then + n = n + 65536 + end + table.insert(strary, tostr(h, math.floor(n / 256), n % 256)) end -local function strary_append_int32(n,h) - if n < 0 then - n = n + 4294967296 - end - table.insert(strary, tostr(h, - math.floor(n / 16777216), - math.floor(n / 65536) % 256, - math.floor(n / 256) % 256, - n % 256 )) +local function strary_append_int32(n, h) + if n < 0 then + n = n + 4294967296 + end + table.insert( + strary, + tostr(h, math.floor(n / 16777216), math.floor(n / 65536) % 256, math.floor(n / 256) % 256, n % 256) + ) end local doubleto8bytes local strary_append_double = function(n) - -- assume double - double_encode_count = double_encode_count + 1 - local b = doubleto8bytes(n) - table.insert( strary, tostr(0xcb)) - table.insert( strary, string.reverse(b) ) -- reverse: make big endian double precision + -- assume double + double_encode_count = double_encode_count + 1 + local b = doubleto8bytes(n) + table.insert(strary, tostr(0xcb)) + table.insert(strary, string.reverse(b)) -- reverse: make big endian double precision end --- IEEE 754 -- out little endian doubleto8bytes = function(x) - local function grab_byte(v) - return math.floor(v / 256), tostr(math.fmod(math.floor(v), 256)) - end - local sign = 0 - if x < 0 then sign = 1; x = -x end - local mantissa, exponent = math.frexp(x) - if x == 0 then -- zero - mantissa, exponent = 0, 0 - elseif x == 1/0 then - mantissa, exponent = 0, 2047 - else - mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53) - exponent = exponent + 1022 - end + local function grab_byte(v) + return math.floor(v / 256), tostr(math.fmod(math.floor(v), 256)) + end + local sign = 0 + if x < 0 then + sign = 1 + x = -x + end + local mantissa, exponent = math.frexp(x) + if x == 0 then -- zero + mantissa, exponent = 0, 0 + elseif x == 1 / 0 then + mantissa, exponent = 0, 2047 + else + mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53) + exponent = exponent + 1022 + end - local v, byte = "" -- convert to bytes - x = mantissa - for _ = 1,6 do - _, byte = grab_byte(x); v = v..byte -- 47:0 - end - x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48 - x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56 - return v, x + local v, byte = "" -- convert to bytes + x = mantissa + for _ = 1, 6 do + _, byte = grab_byte(x) + v = v .. byte -- 47:0 + end + x, byte = grab_byte(exponent * 16 + x) + v = v .. byte -- 55:48 + x, byte = grab_byte(sign * 128 + x) + v = v .. byte -- 63:56 + return v, x end local function bitstofrac(ary) - local x = 0 - local cur = 0.5 - for _, v in ipairs(ary) do - x = x + cur * v - cur = cur / 2 - end - return x + local x = 0 + local cur = 0.5 + for _, v in ipairs(ary) do + x = x + cur * v + cur = cur / 2 + end + return x end local function bytestobits(ary) - local out={} - for _, v in ipairs(ary) do - for j = 0, 7, 1 do - table.insert(out, band( rshift(v,7-j), 1 ) ) - end - end - return out + local out = {} + for _, v in ipairs(ary) do + for j = 0, 7, 1 do + table.insert(out, band(rshift(v, 7 - j), 1)) + end + end + return out end -- get little endian local function bytestodouble(v) - -- sign:1bit - -- exp: 11bit (2048, bias=1023) - local sign = math.floor(v:byte(8) / 128) - local exp = band( v:byte(8), 127 ) * 16 + rshift( v:byte(7), 4 ) - 1023 -- bias - -- frac: 52 bit - local fracbytes = { - band( v:byte(7), 15 ), v:byte(6), v:byte(5), v:byte(4), v:byte(3), v:byte(2), v:byte(1) -- big endian - } - local bits = bytestobits(fracbytes) + -- sign:1bit + -- exp: 11bit (2048, bias=1023) + local sign = math.floor(v:byte(8) / 128) + local exp = band(v:byte(8), 127) * 16 + rshift(v:byte(7), 4) - 1023 -- bias + -- frac: 52 bit + local fracbytes = { + band(v:byte(7), 15), + v:byte(6), + v:byte(5), + v:byte(4), + v:byte(3), + v:byte(2), + v:byte(1), -- big endian + } + local bits = bytestobits(fracbytes) - for _ = 1, 4 do table.remove(bits,1) end + for _ = 1, 4 do + table.remove(bits, 1) + end - if sign == 1 then sign = -1 else sign = 1 end + if sign == 1 then + sign = -1 + else + sign = 1 + end - local frac = bitstofrac(bits) - if exp == -1023 and frac==0 then return 0 end - if exp == 1024 and frac==0 then return 1/0 *sign end + local frac = bitstofrac(bits) + if exp == -1023 and frac == 0 then + return 0 + end + if exp == 1024 and frac == 0 then + return 1 / 0 * sign + end - local real = math.ldexp(1+frac,exp) + local real = math.ldexp(1 + frac, exp) - return real * sign + return real * sign end --- packers @@ -130,265 +150,285 @@ end local packers = {} packers.dynamic = function(data) - local t = type(data) - return packers[t](data) + local t = type(data) + return packers[t](data) end packers["nil"] = function() - table.insert( strary, tostr(0xc0)) + table.insert(strary, tostr(0xc0)) end packers.boolean = function(data) - if data then -- pack true - table.insert( strary, tostr(0xc3)) - else -- pack false - table.insert( strary, tostr(0xc2)) - end + if data then -- pack true + table.insert(strary, tostr(0xc3)) + else -- pack false + table.insert(strary, tostr(0xc2)) + end end packers.number = function(n) - if math.floor(n) == n then -- integer - if n >= 0 then -- positive integer - if n < 128 then -- positive fixnum - table.insert( strary, tostr(n)) - elseif n < 256 then -- uint8 - table.insert(strary, tostr(0xcc,n)) - elseif n < 65536 then -- uint16 - strary_append_int16(n,0xcd) - elseif n < 4294967296 then -- uint32 - strary_append_int32(n,0xce) - else -- lua cannot handle uint64, so double - strary_append_double(n) - end - else -- negative integer - if n >= -32 then -- negative fixnum - table.insert( strary, tostr( 0xe0 + ((n+256)%32)) ) - elseif n >= -128 then -- int8 - table.insert( strary, tostr(0xd0,byte_mod(n,0x100))) - elseif n >= -32768 then -- int16 - strary_append_int16(n,0xd1) - elseif n >= -2147483648 then -- int32 - strary_append_int32(n,0xd2) - else -- lua cannot handle int64, so double - strary_append_double(n) - end - end - else -- floating point - strary_append_double(n) - end + if math.floor(n) == n then -- integer + if n >= 0 then -- positive integer + if n < 128 then -- positive fixnum + table.insert(strary, tostr(n)) + elseif n < 256 then -- uint8 + table.insert(strary, tostr(0xcc, n)) + elseif n < 65536 then -- uint16 + strary_append_int16(n, 0xcd) + elseif n < 4294967296 then -- uint32 + strary_append_int32(n, 0xce) + else -- lua cannot handle uint64, so double + strary_append_double(n) + end + else -- negative integer + if n >= -32 then -- negative fixnum + table.insert(strary, tostr(0xe0 + ((n + 256) % 32))) + elseif n >= -128 then -- int8 + table.insert(strary, tostr(0xd0, byte_mod(n, 0x100))) + elseif n >= -32768 then -- int16 + strary_append_int16(n, 0xd1) + elseif n >= -2147483648 then -- int32 + strary_append_int32(n, 0xd2) + else -- lua cannot handle int64, so double + strary_append_double(n) + end + end + else -- floating point + strary_append_double(n) + end end packers.string = function(data) - local n = #data - if n < 32 then - table.insert( strary, tostr( 0xa0+n ) ) - elseif n < 65536 then - strary_append_int16(n,0xda) - elseif n < 4294967296 then - strary_append_int32(n,0xdb) - else - error("overflow") - end - table.insert( strary, data) + local n = #data + if n < 32 then + table.insert(strary, tostr(0xa0 + n)) + elseif n < 65536 then + strary_append_int16(n, 0xda) + elseif n < 4294967296 then + strary_append_int32(n, 0xdb) + else + error("overflow") + end + table.insert(strary, data) end packers["function"] = function() - error("unimplemented:function") + error("unimplemented:function") end packers.userdata = function() - error("unimplemented:userdata") + error("unimplemented:userdata") end packers.thread = function() - error("unimplemented:thread") + error("unimplemented:thread") end packers.table = function(data) - local is_map,ndata,nmax = false,0,0 - for k,_ in pairs(data) do - if type(k) == "number" then - if k > nmax then nmax = k end - else is_map = true end - ndata = ndata+1 - end - if is_map then -- pack as map - if ndata < 16 then - table.insert( strary, tostr(0x80+ndata)) - elseif ndata < 65536 then - strary_append_int16(ndata,0xde) - elseif ndata < 4294967296 then - strary_append_int32(ndata,0xdf) - else - error("overflow") - end - for k,v in pairs(data) do - packers[type(k)](k) - packers[type(v)](v) - end - else -- pack as array - if nmax < 16 then - table.insert( strary, tostr( 0x90+nmax ) ) - elseif nmax < 65536 then - strary_append_int16(nmax,0xdc) - elseif nmax < 4294967296 then - strary_append_int32(nmax,0xdd) - else - error("overflow") - end - for i=1,nmax do packers[type(data[i])](data[i]) end - end + local is_map, ndata, nmax = false, 0, 0 + for k, _ in pairs(data) do + if type(k) == "number" then + if k > nmax then + nmax = k + end + else + is_map = true + end + ndata = ndata + 1 + end + if is_map then -- pack as map + if ndata < 16 then + table.insert(strary, tostr(0x80 + ndata)) + elseif ndata < 65536 then + strary_append_int16(ndata, 0xde) + elseif ndata < 4294967296 then + strary_append_int32(ndata, 0xdf) + else + error("overflow") + end + for k, v in pairs(data) do + packers[type(k)](k) + packers[type(v)](v) + end + else -- pack as array + if nmax < 16 then + table.insert(strary, tostr(0x90 + nmax)) + elseif nmax < 65536 then + strary_append_int16(nmax, 0xdc) + elseif nmax < 4294967296 then + strary_append_int32(nmax, 0xdd) + else + error("overflow") + end + for i = 1, nmax do + packers[type(data[i])](data[i]) + end + end end -- types decoding local types_map = { - [0xc0] = "nil", - [0xc2] = "false", - [0xc3] = "true", - [0xca] = "float", - [0xcb] = "double", - [0xcc] = "uint8", - [0xcd] = "uint16", - [0xce] = "uint32", - [0xcf] = "uint64", - [0xd0] = "int8", - [0xd1] = "int16", - [0xd2] = "int32", - [0xd3] = "int64", - [0xda] = "raw16", - [0xdb] = "raw32", - [0xdc] = "array16", - [0xdd] = "array32", - [0xde] = "map16", - [0xdf] = "map32", + [0xc0] = "nil", + [0xc2] = "false", + [0xc3] = "true", + [0xca] = "float", + [0xcb] = "double", + [0xcc] = "uint8", + [0xcd] = "uint16", + [0xce] = "uint32", + [0xcf] = "uint64", + [0xd0] = "int8", + [0xd1] = "int16", + [0xd2] = "int32", + [0xd3] = "int64", + [0xda] = "raw16", + [0xdb] = "raw32", + [0xdc] = "array16", + [0xdd] = "array32", + [0xde] = "map16", + [0xdf] = "map32", } local type_for = function(n) - - if types_map[n] then return types_map[n] - elseif n < 0xc0 then - if n < 0x80 then return "fixnum_posi" - elseif n < 0x90 then return "fixmap" - elseif n < 0xa0 then return "fixarray" - else return "fixraw" end - elseif n > 0xdf then return "fixnum_neg" - else return "undefined" end + if types_map[n] then + return types_map[n] + elseif n < 0xc0 then + if n < 0x80 then + return "fixnum_posi" + elseif n < 0x90 then + return "fixmap" + elseif n < 0xa0 then + return "fixarray" + else + return "fixraw" + end + elseif n > 0xdf then + return "fixnum_neg" + else + return "undefined" + end end local types_len_map = { - uint16 = 2, uint32 = 4, uint64 = 8, - int16 = 2, int32 = 4, int64 = 8, - float = 4, double = 8, + uint16 = 2, + uint32 = 4, + uint64 = 8, + int16 = 2, + int32 = 4, + int64 = 8, + float = 4, + double = 8, } - - - --- unpackers local unpackers = {} -local unpack_number = function(offset,ntype,nlen) - local b1,b2,b3,b4,b5,b6,b7,b8 - if nlen>=2 then - b1,b2 = string.byte( strbuf, offset+1, offset+2 ) - end - if nlen>=4 then - b3,b4 = string.byte( strbuf, offset+3, offset+4 ) - end - if nlen>=8 then - b5,b6,b7,b8 = string.byte( strbuf, offset+5, offset+8 ) - end +local unpack_number = function(offset, ntype, nlen) + local b1, b2, b3, b4, b5, b6, b7, b8 + if nlen >= 2 then + b1, b2 = string.byte(strbuf, offset + 1, offset + 2) + end + if nlen >= 4 then + b3, b4 = string.byte(strbuf, offset + 3, offset + 4) + end + if nlen >= 8 then + b5, b6, b7, b8 = string.byte(strbuf, offset + 5, offset + 8) + end - if ntype == "uint16_t" then - return b1 * 256 + b2 - elseif ntype == "uint32_t" then - return b1*65536*256 + b2*65536 + b3 * 256 + b4 - elseif ntype == "int16_t" then - local n = b1 * 256 + b2 - local nn = (65536 - n)*-1 - if nn == -65536 then nn = 0 end - return nn - elseif ntype == "int32_t" then - local n = b1*65536*256 + b2*65536 + b3 * 256 + b4 - local nn = ( 4294967296 - n ) * -1 - if nn == -4294967296 then nn = 0 end - return nn - elseif ntype == "double_t" then - local s = tostr(b8,b7,b6,b5,b4,b3,b2,b1) - double_decode_count = double_decode_count + 1 - local n = bytestodouble( s ) - return n - else - error("unpack_number: not impl:" .. ntype ) - end + if ntype == "uint16_t" then + return b1 * 256 + b2 + elseif ntype == "uint32_t" then + return b1 * 65536 * 256 + b2 * 65536 + b3 * 256 + b4 + elseif ntype == "int16_t" then + local n = b1 * 256 + b2 + local nn = (65536 - n) * -1 + if nn == -65536 then + nn = 0 + end + return nn + elseif ntype == "int32_t" then + local n = b1 * 65536 * 256 + b2 * 65536 + b3 * 256 + b4 + local nn = (4294967296 - n) * -1 + if nn == -4294967296 then + nn = 0 + end + return nn + elseif ntype == "double_t" then + local s = tostr(b8, b7, b6, b5, b4, b3, b2, b1) + double_decode_count = double_decode_count + 1 + local n = bytestodouble(s) + return n + else + error("unpack_number: not impl:" .. ntype) + end end - - local function unpacker_number(offset) - local obj_type = type_for( string.byte( strbuf, offset+1, offset+1 ) ) - local nlen = types_len_map[obj_type] - local ntype - if (obj_type == "float") then - error("float is not implemented") - else - ntype = obj_type .. "_t" - end - return offset+nlen+1,unpack_number(offset+1,ntype,nlen) + local obj_type = type_for(string.byte(strbuf, offset + 1, offset + 1)) + local nlen = types_len_map[obj_type] + local ntype + if obj_type == "float" then + error("float is not implemented") + else + ntype = obj_type .. "_t" + end + return offset + nlen + 1, unpack_number(offset + 1, ntype, nlen) end -local function unpack_map(offset,n) - local r = {} - local k,v - for _ = 1, n do - offset,k = unpackers.dynamic(offset) - assert(offset) - offset,v = unpackers.dynamic(offset) - assert(offset) - r[k] = v - end - return offset,r +local function unpack_map(offset, n) + local r = {} + local k, v + for _ = 1, n do + offset, k = unpackers.dynamic(offset) + assert(offset) + offset, v = unpackers.dynamic(offset) + assert(offset) + r[k] = v + end + return offset, r end -local function unpack_array(offset,n) - local r = {} - for i=1,n do - offset,r[i] = unpackers.dynamic(offset) - assert(offset) - end - return offset,r +local function unpack_array(offset, n) + local r = {} + for i = 1, n do + offset, r[i] = unpackers.dynamic(offset) + assert(offset) + end + return offset, r end function unpackers.dynamic(offset) - if offset >= #strbuf then error("need more data") end - local obj_type = type_for( string.byte( strbuf, offset+1, offset+1 ) ) - return unpackers[obj_type](offset) + if offset >= #strbuf then + error("need more data") + end + local obj_type = type_for(string.byte(strbuf, offset + 1, offset + 1)) + return unpackers[obj_type](offset) end function unpackers.undefined() - error("unimplemented:undefined") + error("unimplemented:undefined") end unpackers["nil"] = function(offset) - return offset+1,nil + return offset + 1, nil end unpackers["false"] = function(offset) - return offset+1,false + return offset + 1, false end unpackers["true"] = function(offset) - return offset+1,true + return offset + 1, true end unpackers.fixnum_posi = function(offset) - return offset+1, string.byte(strbuf, offset+1, offset+1) + return offset + 1, string.byte(strbuf, offset + 1, offset + 1) end unpackers.uint8 = function(offset) - return offset+2, string.byte(strbuf, offset+2, offset+2) + return offset + 2, string.byte(strbuf, offset + 2, offset + 2) end unpackers.uint16 = unpacker_number @@ -396,18 +436,18 @@ unpackers.uint32 = unpacker_number unpackers.uint64 = unpacker_number unpackers.fixnum_neg = function(offset) - -- alternative to cast below: - local n = string.byte( strbuf, offset+1, offset+1) - local nn = ( 256 - n ) * -1 - return offset+1, nn + -- alternative to cast below: + local n = string.byte(strbuf, offset + 1, offset + 1) + local nn = (256 - n) * -1 + return offset + 1, nn end unpackers.int8 = function(offset) - local i = string.byte( strbuf, offset+2, offset+2 ) - if i > 127 then - i = (256 - i ) * -1 - end - return offset+2, i + local i = string.byte(strbuf, offset + 2, offset + 2) + if i > 127 then + i = (256 - i) * -1 + end + return offset + 2, i end unpackers.int16 = unpacker_number @@ -418,92 +458,96 @@ unpackers.float = unpacker_number unpackers.double = unpacker_number unpackers.fixraw = function(offset) - local n = byte_mod( string.byte( strbuf, offset+1, offset+1) ,0x1f+1) - -- print("unpackers.fixraw: offset:", offset, "#buf:", #buf, "n:",n ) - local b - if ( #strbuf - 1 - offset ) < n then - error("require more data") - end + local n = byte_mod(string.byte(strbuf, offset + 1, offset + 1), 0x1f + 1) + -- print("unpackers.fixraw: offset:", offset, "#buf:", #buf, "n:",n ) + local b + if (#strbuf - 1 - offset) < n then + error("require more data") + end - if n > 0 then - b = string.sub( strbuf, offset + 1 + 1, offset + 1 + 1 + n - 1 ) - else - b = "" - end - return offset+n+1, b + if n > 0 then + b = string.sub(strbuf, offset + 1 + 1, offset + 1 + 1 + n - 1) + else + b = "" + end + return offset + n + 1, b end unpackers.raw16 = function(offset) - local n = unpack_number(offset+1,"uint16_t",2) - if ( #strbuf - 1 - 2 - offset ) < n then - error("require more data") - end - local b = string.sub( strbuf, offset+1+1+2, offset+1 + 1+2 + n - 1 ) - return offset+n+3, b + local n = unpack_number(offset + 1, "uint16_t", 2) + if (#strbuf - 1 - 2 - offset) < n then + error("require more data") + end + local b = string.sub(strbuf, offset + 1 + 1 + 2, offset + 1 + 1 + 2 + n - 1) + return offset + n + 3, b end unpackers.raw32 = function(offset) - local n = unpack_number(offset+1,"uint32_t",4) - if ( #strbuf - 1 - 4 - offset ) < n then - error( "require more data (possibly bug)") - end - local b = string.sub( strbuf, offset+1+ 1+4, offset+1 + 1+4 +n -1 ) - return offset+n+5,b + local n = unpack_number(offset + 1, "uint32_t", 4) + if (#strbuf - 1 - 4 - offset) < n then + error("require more data (possibly bug)") + end + local b = string.sub(strbuf, offset + 1 + 1 + 4, offset + 1 + 1 + 4 + n - 1) + return offset + n + 5, b end unpackers.fixarray = function(offset) - return unpack_array( offset+1,byte_mod( string.byte( strbuf, offset+1,offset+1),0x0f+1)) + return unpack_array(offset + 1, byte_mod(string.byte(strbuf, offset + 1, offset + 1), 0x0f + 1)) end unpackers.array16 = function(offset) - return unpack_array(offset+3,unpack_number(offset+1,"uint16_t",2)) + return unpack_array(offset + 3, unpack_number(offset + 1, "uint16_t", 2)) end unpackers.array32 = function(offset) - return unpack_array(offset+5,unpack_number(offset+1,"uint32_t",4)) + return unpack_array(offset + 5, unpack_number(offset + 1, "uint32_t", 4)) end unpackers.fixmap = function(offset) - return unpack_map(offset+1,byte_mod( string.byte( strbuf, offset+1,offset+1),0x0f+1)) + return unpack_map(offset + 1, byte_mod(string.byte(strbuf, offset + 1, offset + 1), 0x0f + 1)) end unpackers.map16 = function(offset) - return unpack_map(offset+3,unpack_number(offset+1,"uint16_t",2)) + return unpack_map(offset + 3, unpack_number(offset + 1, "uint16_t", 2)) end unpackers.map32 = function(offset) - return unpack_map(offset+5,unpack_number(offset+1,"uint32_t",4)) + return unpack_map(offset + 5, unpack_number(offset + 1, "uint32_t", 4)) end -- Main functions local ljp_pack = function(data) - strary={} - packers.dynamic(data) - local s = table.concat(strary,"") - return s + strary = {} + packers.dynamic(data) + local s = table.concat(strary, "") + return s end -local ljp_unpack = function(s,offset) - if offset == nil then offset = 0 end - if type(s) ~= "string" then return false,"invalid argument" end - local data - strbuf = s - offset,data = unpackers.dynamic(offset) - return offset,data +local ljp_unpack = function(s, offset) + if offset == nil then + offset = 0 + end + if type(s) ~= "string" then + return false, "invalid argument" + end + local data + strbuf = s + offset, data = unpackers.dynamic(offset) + return offset, data end local function ljp_stat() - return { - double_decode_count = double_decode_count, - double_encode_count = double_encode_count - } + return { + double_decode_count = double_decode_count, + double_encode_count = double_encode_count, + } end local msgpack = { - pack = ljp_pack, - unpack = ljp_unpack, - stat = ljp_stat + pack = ljp_pack, + unpack = ljp_unpack, + stat = ljp_stat, } return msgpack diff --git a/lua/deps/serpent.lua b/lua/deps/serpent.lua index a043713..4503686 100644 --- a/lua/deps/serpent.lua +++ b/lua/deps/serpent.lua @@ -1,140 +1,265 @@ local n, v = "serpent", "0.302" -- (C) 2012-18 Paul Kulchenko; MIT License local c, d = "Paul Kulchenko", "Lua serializer and pretty printer" -local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'} -local badtype = {thread = true, userdata = true, cdata = true} +local snum = { + [tostring(1 / 0)] = "1/0 --[[math.huge]]", + [tostring(-1 / 0)] = "-1/0 --[[-math.huge]]", + [tostring(0 / 0)] = "0/0", +} +local badtype = { thread = true, userdata = true, cdata = true } local getmetatable = debug and debug.getmetatable or getmetatable -local pairs = function(t) return next, t end -- avoid using __pairs in Lua 5.2+ +local pairs = function(t) + return next, t +end -- avoid using __pairs in Lua 5.2+ local keyword, globals, G = {}, {}, (_G or _ENV) -for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false', - 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', - 'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end -for k,v in pairs(G) do globals[v] = k end -- build func to name mapping -for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do - for k,v in pairs(type(G[g]) == 'table' and G[g] or {}) do globals[v] = g..'.'..k end end +for _, k in ipairs({ + "and", + "break", + "do", + "else", + "elseif", + "end", + "false", + "for", + "function", + "goto", + "if", + "in", + "local", + "nil", + "not", + "or", + "repeat", + "return", + "then", + "true", + "until", + "while", +}) do + keyword[k] = true +end +for k, v in pairs(G) do + globals[v] = k +end -- build func to name mapping +for _, g in ipairs({ "coroutine", "debug", "io", "math", "string", "table", "os" }) do + for k, v in pairs(type(G[g]) == "table" and G[g] or {}) do + globals[v] = g .. "." .. k + end +end local function s(t, opts) - local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum - local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge - local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge) - local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring - local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge) - local numformat = opts.numformat or "%.17g" - local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0 - local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)", - -- tostring(val) is needed because __tostring may return a non-string value - function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return tostring(syms[s]) end)) end - local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s)) - or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 - or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end - local function comment(s,l) return comm and (l or 0) < comm and ' --[['..select(2, pcall(tostring, s))..']]' or '' end - local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal - and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end - local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] - local n = name == nil and '' or name - local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] - local safe = plain and n or '['..safestr(n)..']' - return (path or '')..(plain and path and '.' or '')..safe, safe end - local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding - local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'} - local function padnum(d) return ("%0"..tostring(maxn).."d"):format(tonumber(d)) end - table.sort(k, function(a,b) - -- sort numeric keys first: k[key] is not nil for numerical keys - return (k[a] ~= nil and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum)) - < (k[b] ~= nil and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end - local function val2str(t, name, indent, insref, path, plainindex, level) - local ttype, level, mt = type(t), (level or 0), getmetatable(t) - local spath, sname = safename(path, name) - local tag = plainindex and - ((type(name) == "number") and '' or name..space..'='..space) or - (name ~= nil and sname..space..'='..space or '') - if seen[t] then -- already seen this element - sref[#sref+1] = spath..space..'='..space..seen[t] - return tag..'nil'..comment('ref', level) end - -- protect from those cases where __tostring may fail - if type(mt) == 'table' and metatostring ~= false then - local to, tr = pcall(function() return mt.__tostring(t) end) - local so, sr = pcall(function() return mt.__serialize(t) end) - if (to or so) then -- knows how to serialize itself - seen[t] = insref or spath - t = so and sr or tr - ttype = type(t) - end -- new value falls through to be serialized - end - if ttype == "table" then - if level >= maxl then return tag..'{}'..comment('maxlvl', level) end - seen[t] = insref or spath - if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty - if maxlen and maxlen < 0 then return tag..'{}'..comment('maxlen', level) end - local maxn, o, out = math.min(#t, maxnum or #t), {}, {} - for key = 1, maxn do o[key] = key end - if not maxnum or #o < maxnum then - local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables - for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end - if maxnum and #o > maxnum then o[maxnum+1] = nil end - if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end - local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) - for n, key in ipairs(o) do - local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse - if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing - or opts.keyallow and not opts.keyallow[key] - or opts.keyignore and opts.keyignore[key] - or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types - or sparse and value == nil then -- skipping nils; do nothing - elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then - if not seen[key] and not globals[key] then - sref[#sref+1] = 'placeholder' - local sname = safename(iname, gensym(key)) -- iname is table for local variables - sref[#sref] = val2str(key,sname,indent,sname,iname,true) end - sref[#sref+1] = 'placeholder' - local path = seen[t]..'['..tostring(seen[key] or globals[key] or gensym(key))..']' - sref[#sref] = path..space..'='..space..tostring(seen[value] or val2str(value,nil,indent,path)) - else - out[#out+1] = val2str(value,key,indent,nil,seen[t],plainindex,level+1) - if maxlen then - maxlen = maxlen - #out[#out] - if maxlen < 0 then break end - end - end - end - local prefix = string.rep(indent or '', level) - local head = indent and '{\n'..prefix..indent or '{' - local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space)) - local tail = indent and "\n"..prefix..'}' or '}' - return (custom and custom(tag,head,body,tail,level) or tag..head..body..tail)..comment(t, level) - elseif badtype[ttype] then - seen[t] = insref or spath - return tag..globerr(t, level) - elseif ttype == 'function' then - seen[t] = insref or spath - if opts.nocode then return tag.."function() --[[..skipped..]] end"..comment(t, level) end - local ok, res = pcall(string.dump, t) - local func = ok and "((loadstring or load)("..safestr(res)..",'@serialized'))"..comment(t, level) - return tag..(func or globerr(t, level)) - else return tag..safestr(t) end -- handle all other types - end - local sepr = indent and "\n" or ";"..space - local body = val2str(t, name, indent) -- this call also populates sref - local tail = #sref>1 and table.concat(sref, sepr)..sepr or '' - local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or '' - return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end" + local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum + local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge + local space, maxl = (opts.compact and "" or " "), (opts.maxlevel or math.huge) + local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring + local iname, comm = "_" .. (name or ""), opts.comment and (tonumber(opts.comment) or math.huge) + local numformat = opts.numformat or "%.17g" + local seen, sref, syms, symn = {}, { "local " .. iname .. "={}" }, {}, 0 + local function gensym(val) + return "_" + .. ( + tostring(tostring(val)):gsub("[^%w]", ""):gsub( + "(%d%w+)", + -- tostring(val) is needed because __tostring may return a non-string value + function(s) + if not syms[s] then + symn = symn + 1 + syms[s] = symn + end + return tostring(syms[s]) + end + ) + ) + end + local function safestr(s) + return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s)) + or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 + or ("%q"):format(s):gsub("\010", "n"):gsub("\026", "\\026") + end + local function comment(s, l) + return comm and (l or 0) < comm and " --[[" .. select(2, pcall(tostring, s)) .. "]]" or "" + end + local function globerr(s, l) + return globals[s] and globals[s] .. comment(s, l) + or not fatal and safestr(select(2, pcall(tostring, s))) + or error("Can't serialize " .. tostring(s)) + end + local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] + local n = name == nil and "" or name + local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] + local safe = plain and n or "[" .. safestr(n) .. "]" + return (path or "") .. (plain and path and "." or "") .. safe, safe + end + local alphanumsort = type(opts.sortkeys) == "function" and opts.sortkeys + or function(k, o, n) -- k=keys, o=originaltable, n=padding + local maxn, to = tonumber(n) or 12, { number = "a", string = "b" } + local function padnum(d) + return ("%0" .. tostring(maxn) .. "d"):format(tonumber(d)) + end + table.sort(k, function(a, b) + -- sort numeric keys first: k[key] is not nil for numerical keys + return (k[a] ~= nil and 0 or to[type(a)] or "z") .. (tostring(a):gsub("%d+", padnum)) + < (k[b] ~= nil and 0 or to[type(b)] or "z") .. (tostring(b):gsub("%d+", padnum)) + end) + end + local function val2str(t, name, indent, insref, path, plainindex, level) + local ttype, level, mt = type(t), (level or 0), getmetatable(t) + local spath, sname = safename(path, name) + local tag = plainindex and ((type(name) == "number") and "" or name .. space .. "=" .. space) + or (name ~= nil and sname .. space .. "=" .. space or "") + if seen[t] then -- already seen this element + sref[#sref + 1] = spath .. space .. "=" .. space .. seen[t] + return tag .. "nil" .. comment("ref", level) + end + -- protect from those cases where __tostring may fail + if type(mt) == "table" and metatostring ~= false then + local to, tr = pcall(function() + return mt.__tostring(t) + end) + local so, sr = pcall(function() + return mt.__serialize(t) + end) + if to or so then -- knows how to serialize itself + seen[t] = insref or spath + t = so and sr or tr + ttype = type(t) + end -- new value falls through to be serialized + end + if ttype == "table" then + if level >= maxl then + return tag .. "{}" .. comment("maxlvl", level) + end + seen[t] = insref or spath + if next(t) == nil then + return tag .. "{}" .. comment(t, level) + end -- table empty + if maxlen and maxlen < 0 then + return tag .. "{}" .. comment("maxlen", level) + end + local maxn, o, out = math.min(#t, maxnum or #t), {}, {} + for key = 1, maxn do + o[key] = key + end + if not maxnum or #o < maxnum then + local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables + for key in pairs(t) do + if o[key] ~= key then + n = n + 1 + o[n] = key + end + end + end + if maxnum and #o > maxnum then + o[maxnum + 1] = nil + end + if opts.sortkeys and #o > maxn then + alphanumsort(o, t, opts.sortkeys) + end + local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) + for n, key in ipairs(o) do + local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse + if + opts.valignore and opts.valignore[value] -- skip ignored values; do nothing + or opts.keyallow and not opts.keyallow[key] + or opts.keyignore and opts.keyignore[key] + or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types + or sparse and value == nil + then -- skipping nils; do nothing + elseif ktype == "table" or ktype == "function" or badtype[ktype] then + if not seen[key] and not globals[key] then + sref[#sref + 1] = "placeholder" + local sname = safename(iname, gensym(key)) -- iname is table for local variables + sref[#sref] = val2str(key, sname, indent, sname, iname, true) + end + sref[#sref + 1] = "placeholder" + local path = seen[t] .. "[" .. tostring(seen[key] or globals[key] or gensym(key)) .. "]" + sref[#sref] = path + .. space + .. "=" + .. space + .. tostring(seen[value] or val2str(value, nil, indent, path)) + else + out[#out + 1] = val2str(value, key, indent, nil, seen[t], plainindex, level + 1) + if maxlen then + maxlen = maxlen - #out[#out] + if maxlen < 0 then + break + end + end + end + end + local prefix = string.rep(indent or "", level) + local head = indent and "{\n" .. prefix .. indent or "{" + local body = table.concat(out, "," .. (indent and "\n" .. prefix .. indent or space)) + local tail = indent and "\n" .. prefix .. "}" or "}" + return (custom and custom(tag, head, body, tail, level) or tag .. head .. body .. tail) .. comment(t, level) + elseif badtype[ttype] then + seen[t] = insref or spath + return tag .. globerr(t, level) + elseif ttype == "function" then + seen[t] = insref or spath + if opts.nocode then + return tag .. "function() --[[..skipped..]] end" .. comment(t, level) + end + local ok, res = pcall(string.dump, t) + local func = ok and "((loadstring or load)(" .. safestr(res) .. ",'@serialized'))" .. comment(t, level) + return tag .. (func or globerr(t, level)) + else + return tag .. safestr(t) + end -- handle all other types + end + local sepr = indent and "\n" or ";" .. space + local body = val2str(t, name, indent) -- this call also populates sref + local tail = #sref > 1 and table.concat(sref, sepr) .. sepr or "" + local warn = opts.comment and #sref > 1 and space .. "--[[incomplete output with shared/self-references skipped]]" + or "" + return not name and body .. warn or "do local " .. body .. sepr .. tail .. "return " .. name .. sepr .. "end" end local function deserialize(data, opts) - local env = (opts and opts.safe == false) and G - or setmetatable({}, { - __index = function(t,k) return t end, - __call = function(t,...) error("cannot call functions") end - }) - local f, res = (loadstring or load)('return '..data, nil, nil, env) - if not f then f, res = (loadstring or load)(data, nil, nil, env) end - if not f then return f, res end - if setfenv then setfenv(f, env) end - return pcall(f) + local env = (opts and opts.safe == false) and G + or setmetatable({}, { + __index = function(t, k) + return t + end, + __call = function(t, ...) + error("cannot call functions") + end, + }) + local f, res = (loadstring or load)("return " .. data, nil, nil, env) + if not f then + f, res = (loadstring or load)(data, nil, nil, env) + end + if not f then + return f, res + end + if setfenv then + setfenv(f, env) + end + return pcall(f) end -local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end -return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s, - load = deserialize, - dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end, - line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end, - block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end } +local function merge(a, b) + if b then + for k, v in pairs(b) do + a[k] = v + end + end + return a +end +return { + _NAME = n, + _COPYRIGHT = c, + _DESCRIPTION = d, + _VERSION = v, + serialize = s, + load = deserialize, + dump = function(a, opts) + return s(a, merge({ name = "_", compact = true, sparse = true }, opts)) + end, + line = function(a, opts) + return s(a, merge({ sortkeys = true, comment = true }, opts)) + end, + block = function(a, opts) + return s(a, merge({ indent = " ", sortkeys = true, comment = true }, opts)) + end, +} diff --git a/lua/deps/struct.lua b/lua/deps/struct.lua index 54b32d4..1b0e814 100644 --- a/lua/deps/struct.lua +++ b/lua/deps/struct.lua @@ -1,179 +1,178 @@ local struct = {} function struct.pack(format, ...) - local stream = {} - local vars = {...} - local endianness = true + local stream = {} + local vars = { ... } + local endianness = true - for i = 1, format:len() do - local opt = format:sub(i, i) + for i = 1, format:len() do + local opt = format:sub(i, i) - if opt == '<' then - endianness = true - elseif opt == '>' then - endianness = false - elseif opt:find('[bBhHiIlL]') then - local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1 - local val = tonumber(table.remove(vars, 1)) + if opt == "<" then + endianness = true + elseif opt == ">" then + endianness = false + elseif opt:find("[bBhHiIlL]") then + local n = opt:find("[hH]") and 2 or opt:find("[iI]") and 4 or opt:find("[lL]") and 8 or 1 + local val = tonumber(table.remove(vars, 1)) - local bytes = {} - for _ = 1, n do - table.insert(bytes, string.char(val % (2 ^ 8))) - val = math.floor(val / (2 ^ 8)) - end + local bytes = {} + for _ = 1, n do + table.insert(bytes, string.char(val % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end - if not endianness then - table.insert(stream, string.reverse(table.concat(bytes))) - else - table.insert(stream, table.concat(bytes)) - end - elseif opt:find('[fd]') then - local val = tonumber(table.remove(vars, 1)) - local sign = 0 + if not endianness then + table.insert(stream, string.reverse(table.concat(bytes))) + else + table.insert(stream, table.concat(bytes)) + end + elseif opt:find("[fd]") then + local val = tonumber(table.remove(vars, 1)) + local sign = 0 - if val < 0 then - sign = 1 - val = -val - end + if val < 0 then + sign = 1 + val = -val + end - local mantissa, exponent = math.frexp(val) - if val == 0 then - mantissa = 0 - exponent = 0 - else - mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == 'd') and 53 or 24) - exponent = exponent + ((opt == 'd') and 1022 or 126) - end + local mantissa, exponent = math.frexp(val) + if val == 0 then + mantissa = 0 + exponent = 0 + else + mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == "d") and 53 or 24) + exponent = exponent + ((opt == "d") and 1022 or 126) + end - local bytes = {} - if opt == 'd' then - val = mantissa - for _ = 1, 6 do - table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) - val = math.floor(val / (2 ^ 8)) - end - else - table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8))) - val = math.floor(mantissa / (2 ^ 8)) - table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) - val = math.floor(val / (2 ^ 8)) - end + local bytes = {} + if opt == "d" then + val = mantissa + for _ = 1, 6 do + table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end + else + table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8))) + val = math.floor(mantissa / (2 ^ 8)) + table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end - table.insert(bytes, string.char(math.floor(exponent * ((opt == 'd') and 16 or 128) + val) % (2 ^ 8))) - val = math.floor((exponent * ((opt == 'd') and 16 or 128) + val) / (2 ^ 8)) - table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8))) + table.insert(bytes, string.char(math.floor(exponent * ((opt == "d") and 16 or 128) + val) % (2 ^ 8))) + val = math.floor((exponent * ((opt == "d") and 16 or 128) + val) / (2 ^ 8)) + table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8))) - if not endianness then - table.insert(stream, string.reverse(table.concat(bytes))) - else - table.insert(stream, table.concat(bytes)) - end - elseif opt == 's' then - table.insert(stream, tostring(table.remove(vars, 1))) - table.insert(stream, string.char(0)) - elseif opt == 'c' then - local n = format:sub(i + 1):match('%d+') - local str = tostring(table.remove(vars, 1)) - local len = tonumber(n) - if len <= 0 then - len = str:len() - end - if len - str:len() > 0 then - str = str .. string.rep(' ', len - str:len()) - end - table.insert(stream, str:sub(1, len)) - end - end + if not endianness then + table.insert(stream, string.reverse(table.concat(bytes))) + else + table.insert(stream, table.concat(bytes)) + end + elseif opt == "s" then + table.insert(stream, tostring(table.remove(vars, 1))) + table.insert(stream, string.char(0)) + elseif opt == "c" then + local n = format:sub(i + 1):match("%d+") + local str = tostring(table.remove(vars, 1)) + local len = tonumber(n) + if len <= 0 then + len = str:len() + end + if len - str:len() > 0 then + str = str .. string.rep(" ", len - str:len()) + end + table.insert(stream, str:sub(1, len)) + end + end - return table.concat(stream) + return table.concat(stream) end function struct.unpack(format, stream, pos) - local vars = {} - local iterator = pos or 1 - local endianness = true + local vars = {} + local iterator = pos or 1 + local endianness = true - for i = 1, format:len() do - local opt = format:sub(i, i) + for i = 1, format:len() do + local opt = format:sub(i, i) - if opt == '<' then - endianness = true - elseif opt == '>' then - endianness = false - elseif opt:find('[bBhHiIlL]') then - local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1 - local signed = opt:lower() == opt + if opt == "<" then + endianness = true + elseif opt == ">" then + endianness = false + elseif opt:find("[bBhHiIlL]") then + local n = opt:find("[hH]") and 2 or opt:find("[iI]") and 4 or opt:find("[lL]") and 8 or 1 + local signed = opt:lower() == opt - local val = 0 - for j = 1, n do - local byte = string.byte(stream:sub(iterator, iterator)) - if endianness then - val = val + byte * (2 ^ ((j - 1) * 8)) - else - val = val + byte * (2 ^ ((n - j) * 8)) - end - iterator = iterator + 1 - end + local val = 0 + for j = 1, n do + local byte = string.byte(stream:sub(iterator, iterator)) + if endianness then + val = val + byte * (2 ^ ((j - 1) * 8)) + else + val = val + byte * (2 ^ ((n - j) * 8)) + end + iterator = iterator + 1 + end - if signed and val >= 2 ^ (n * 8 - 1) then - val = val - 2 ^ (n * 8) - end + if signed and val >= 2 ^ (n * 8 - 1) then + val = val - 2 ^ (n * 8) + end - table.insert(vars, math.floor(val)) - elseif opt:find('[fd]') then - local n = (opt == 'd') and 8 or 4 - local x = stream:sub(iterator, iterator + n - 1) - iterator = iterator + n + table.insert(vars, math.floor(val)) + elseif opt:find("[fd]") then + local n = (opt == "d") and 8 or 4 + local x = stream:sub(iterator, iterator + n - 1) + iterator = iterator + n - if not endianness then - x = string.reverse(x) - end + if not endianness then + x = string.reverse(x) + end - local sign = 1 - local mantissa = string.byte(x, (opt == 'd') and 7 or 3) % ((opt == 'd') and 16 or 128) - for j = n - 2, 1, -1 do - mantissa = mantissa * (2 ^ 8) + string.byte(x, j) - end + local sign = 1 + local mantissa = string.byte(x, (opt == "d") and 7 or 3) % ((opt == "d") and 16 or 128) + for j = n - 2, 1, -1 do + mantissa = mantissa * (2 ^ 8) + string.byte(x, j) + end - if string.byte(x, n) > 127 then - sign = -1 - end + if string.byte(x, n) > 127 then + sign = -1 + end - local exponent = (string.byte(x, n) % 128) * ((opt == 'd') and 16 or 2) + - math.floor(string.byte(x, n - 1) / - ((opt == 'd') and 16 or 128)) - if exponent == 0 then - table.insert(vars, 0.0) - else - mantissa = (math.ldexp(mantissa, (opt == 'd') and -52 or -23) + 1) * sign - table.insert(vars, math.ldexp(mantissa, exponent - ((opt == 'd') and 1023 or 127))) - end - elseif opt == 's' then - local bytes = {} - for j = iterator, stream:len() do - if stream:sub(j,j) == string.char(0) or stream:sub(j) == '' then - break - end + local exponent = (string.byte(x, n) % 128) * ((opt == "d") and 16 or 2) + + math.floor(string.byte(x, n - 1) / ((opt == "d") and 16 or 128)) + if exponent == 0 then + table.insert(vars, 0.0) + else + mantissa = (math.ldexp(mantissa, (opt == "d") and -52 or -23) + 1) * sign + table.insert(vars, math.ldexp(mantissa, exponent - ((opt == "d") and 1023 or 127))) + end + elseif opt == "s" then + local bytes = {} + for j = iterator, stream:len() do + if stream:sub(j, j) == string.char(0) or stream:sub(j) == "" then + break + end - table.insert(bytes, stream:sub(j, j)) - end + table.insert(bytes, stream:sub(j, j)) + end - local str = table.concat(bytes) - iterator = iterator + str:len() + 1 - table.insert(vars, str) - elseif opt == 'c' then - local n = format:sub(i + 1):match('%d+') - local len = tonumber(n) - if len <= 0 then - len = table.remove(vars) - end + local str = table.concat(bytes) + iterator = iterator + str:len() + 1 + table.insert(vars, str) + elseif opt == "c" then + local n = format:sub(i + 1):match("%d+") + local len = tonumber(n) + if len <= 0 then + len = table.remove(vars) + end - table.insert(vars, stream:sub(iterator, iterator + len - 1)) - iterator = iterator + len - end - end + table.insert(vars, stream:sub(iterator, iterator + len - 1)) + iterator = iterator + len + end + end - return unpack(vars) + return unpack(vars) end return struct diff --git a/lua/lib/log.lua b/lua/lib/log.lua index b7b71e4..dcacdb3 100644 --- a/lua/lib/log.lua +++ b/lua/lib/log.lua @@ -2,37 +2,37 @@ local Log = {} Log.codes = {} Log.levels = { - { "debug", "Comment" }, - { "info", "None" }, - { "warn", "WarningMsg" }, - { "error", "ErrorMsg" }, + { "debug", "Comment" }, + { "info", "None" }, + { "warn", "WarningMsg" }, + { "error", "ErrorMsg" }, } function Log:init(options) - self.level = options.level - return self + self.level = options.level + return self end -- Initialize logger with log functions for each level for i = 1, #Log.levels do - local level, hl = unpack(Log.levels[i]) + local level, hl = unpack(Log.levels[i]) - Log.codes[level] = i + Log.codes[level] = i - Log[level] = function(self, message) - -- Skip if log level is not set or the log is below the configured or default level - if not self.level or self.codes[level] < self.codes[self.level] or type(message) ~= "string" then - return - end + Log[level] = function(self, message) + -- Skip if log level is not set or the log is below the configured or default level + if not self.level or self.codes[level] < self.codes[self.level] or type(message) ~= "string" then + return + end - vim.schedule(function() - local escaped_message = vim.fn.escape(message, '"'):gsub("\n", "\\n") + vim.schedule(function() + local escaped_message = vim.fn.escape(message, '"'):gsub("\n", "\\n") - vim.cmd(string.format("echohl %s", hl)) - vim.cmd(string.format([[echom "[%s] %s"]], "presence.nvim", escaped_message)) - vim.cmd("echohl NONE") - end) - end + vim.cmd(string.format("echohl %s", hl)) + vim.cmd(string.format([[echom "[%s] %s"]], "presence.nvim", escaped_message)) + vim.cmd("echohl NONE") + end) + end end return Log diff --git a/lua/presence/discord.lua b/lua/presence/discord.lua index 83fd77a..6ab2f55 100644 --- a/lua/presence/discord.lua +++ b/lua/presence/discord.lua @@ -1,9 +1,9 @@ local Discord = {} Discord.opcodes = { - auth = 0, - frame = 1, - closed = 2, + auth = 0, + frame = 1, + closed = 2, } -- Discord RPC Subscription events @@ -11,190 +11,187 @@ Discord.opcodes = { -- Ready: https://discord.com/developers/docs/topics/rpc#ready -- Error: https://discord.com/developers/docs/topics/rpc#error Discord.events = { - READY = "READY", - ERROR = "ERROR", + READY = "READY", + ERROR = "ERROR", } local struct = require("deps.struct") -- Initialize a new Discord RPC client function Discord:init(options) - self.log = options.logger - self.client_id = options.client_id - self.ipc_socket = options.ipc_socket + self.log = options.logger + self.client_id = options.client_id + self.ipc_socket = options.ipc_socket - self.pipe = vim.loop.new_pipe(false) + self.pipe = vim.loop.new_pipe(false) - return self + return self end -- Connect to the local Discord RPC socket -- TODO Might need to check for pipes ranging from discord-ipc-0 to discord-ipc-9: -- https://github.com/discord/discord-rpc/blob/master/documentation/hard-mode.md#notes function Discord:connect(on_connect) - if self.pipe:is_closing() then - self.pipe = vim.loop.new_pipe(false) - end + if self.pipe:is_closing() then + self.pipe = vim.loop.new_pipe(false) + end - self.pipe:connect(self.ipc_socket, on_connect) + self.pipe:connect(self.ipc_socket, on_connect) end function Discord:is_connected() - return self.pipe:is_active() + return self.pipe:is_active() end -- Disconnect from the local Discord RPC socket function Discord:disconnect(on_close) - self.pipe:shutdown() - if not self.pipe:is_closing() then - self.pipe:close(on_close) - end + self.pipe:shutdown() + if not self.pipe:is_closing() then + self.pipe:close(on_close) + end end -- Make a remote procedure call to Discord -- Callback argument in format: on_response(error[, response_table]) function Discord:call(opcode, payload, on_response) - self.encode_json(payload, function(success, body) - if not success then - self.log:warn(string.format("Failed to encode payload: %s", vim.inspect(body))) - return - end + self.encode_json(payload, function(success, body) + if not success then + self.log:warn(string.format("Failed to encode payload: %s", vim.inspect(body))) + return + end - -- Start reading for the response - self.pipe:read_start(function(...) - self:read_message(payload.nonce, on_response, ...) - end) + -- Start reading for the response + self.pipe:read_start(function(...) + self:read_message(payload.nonce, on_response, ...) + end) - -- Construct message denoting little endian, auth opcode, msg length - local message = struct.pack(" 1 - local s = is_plural and "s" or "" - self.log:debug(string.format("Using custom-defined button%s", s)) + -- User configured a static buttons table + if type(self.options.buttons) == "table" then + local is_plural = #self.options.buttons > 1 + local s = is_plural and "s" or "" + self.log:debug(string.format("Using custom-defined button%s", s)) - return self.options.buttons - end + return self.options.buttons + end - -- Retrieve the git repository URL - local repo_url - if parent_dirpath then - -- Escape quotes in the file path - local path = parent_dirpath:gsub([["]], [[\"]]) - local git_url_cmd = "git config --get remote.origin.url" - local cmd = path and string.format([[cd "%s" && %s]], path, git_url_cmd) or git_url_cmd + -- Retrieve the git repository URL + local repo_url + if parent_dirpath then + -- Escape quotes in the file path + local path = parent_dirpath:gsub([["]], [[\"]]) + local git_url_cmd = "git config --get remote.origin.url" + local cmd = path and string.format([[cd "%s" && %s]], path, git_url_cmd) or git_url_cmd - -- Trim and coerce empty string value to null - repo_url = vim.trim(vim.fn.system(cmd)) - repo_url = repo_url ~= "" and repo_url or nil - end + -- Trim and coerce empty string value to null + repo_url = vim.trim(vim.fn.system(cmd)) + repo_url = repo_url ~= "" and repo_url or nil + end - -- User configured a function to dynamically create buttons table - if type(self.options.buttons) == "function" then - self.log:debug("Using custom-defined button config function") - return self.options.buttons(buffer, repo_url) - end + -- User configured a function to dynamically create buttons table + if type(self.options.buttons) == "function" then + self.log:debug("Using custom-defined button config function") + return self.options.buttons(buffer, repo_url) + end - -- Default behavior to show a "View Repository" button if the repo URL is valid - if repo_url then - -- Check if repo url uses short ssh syntax - local domain, project = repo_url:match("^git@(.+):(.+)$") - if domain and project then - self.log:debug(string.format("Repository URL uses short ssh syntax: %s", repo_url)) - repo_url = string.format("https://%s/%s", domain, project) - end + -- Default behavior to show a "View Repository" button if the repo URL is valid + if repo_url then + -- Check if repo url uses short ssh syntax + local domain, project = repo_url:match("^git@(.+):(.+)$") + if domain and project then + self.log:debug(string.format("Repository URL uses short ssh syntax: %s", repo_url)) + repo_url = string.format("https://%s/%s", domain, project) + end - -- Check if repo url uses a valid protocol - local protocols = { - "ftp", - "git", - "http", - "https", - "ssh", - } - local protocol, relative = repo_url:match("^(.+)://(.+)$") - if not vim.tbl_contains(protocols, protocol) or not relative then - self.log:debug(string.format("Repository URL uses invalid protocol: %s", repo_url)) - return nil - end + -- Check if repo url uses a valid protocol + local protocols = { + "ftp", + "git", + "http", + "https", + "ssh", + } + local protocol, relative = repo_url:match("^(.+)://(.+)$") + if not vim.tbl_contains(protocols, protocol) or not relative then + self.log:debug(string.format("Repository URL uses invalid protocol: %s", repo_url)) + return nil + end - -- Check if repo url has the user specified - local user, path = relative:match("^(.+)@(.+)$") - if user and path then - self.log:debug(string.format("Repository URL has user specified: %s", repo_url)) - repo_url = string.format("https://%s", path) - else - repo_url = string.format("https://%s", relative) - end + -- Check if repo url has the user specified + local user, path = relative:match("^(.+)@(.+)$") + if user and path then + self.log:debug(string.format("Repository URL has user specified: %s", repo_url)) + repo_url = string.format("https://%s", path) + else + repo_url = string.format("https://%s", relative) + end - self.log:debug(string.format("Adding button with repository URL: %s", repo_url)) + self.log:debug(string.format("Adding button with repository URL: %s", repo_url)) - return { - { label = "View Repository", url = repo_url }, - } - end + return { + { label = "View Repository", url = repo_url }, + } + end - return nil + return nil end -- Update Rich Presence for the provided vim buffer function Presence:update_for_buffer(buffer, should_debounce) - -- Avoid unnecessary updates if the previous activity was for the current buffer - -- (allow same-buffer updates when line numbers are enabled) - if self.options.enable_line_number == 0 and self.last_activity.file == buffer then - self.log:debug(string.format("Activity already set for %s, skipping...", buffer)) - return - end + -- Avoid unnecessary updates if the previous activity was for the current buffer + -- (allow same-buffer updates when line numbers are enabled) + if self.options.enable_line_number == 0 and self.last_activity.file == buffer then + self.log:debug(string.format("Activity already set for %s, skipping...", buffer)) + return + end - -- Parse vim buffer - local filename = self.get_filename(buffer, self.os.path_separator) - local parent_dirpath = self.get_dir_path(buffer, self.os.path_separator) - local extension = filename and self.get_file_extension(filename) or nil - self.log:debug(string.format("Parsed filename %s with %s extension", filename, extension or "no")) + -- Parse vim buffer + local filename = self.get_filename(buffer, self.os.path_separator) + local parent_dirpath = self.get_dir_path(buffer, self.os.path_separator) + local extension = filename and self.get_file_extension(filename) or nil + self.log:debug(string.format("Parsed filename %s with %s extension", filename, extension or "no")) - -- Return early if there is no valid activity status text to set - local status_text = self:get_status_text(filename) - if not status_text then - return self.log:debug("No status text for the given buffer, skipping...") - end + -- Return early if there is no valid activity status text to set + local status_text = self:get_status_text(filename) + if not status_text then + return self.log:debug("No status text for the given buffer, skipping...") + end - -- Get project information - self.log:debug(string.format("Getting project name for %s...", parent_dirpath)) - local project_name, project_path = self:get_project_name(parent_dirpath) + -- Get project information + self.log:debug(string.format("Getting project name for %s...", parent_dirpath)) + local project_name, project_path = self:get_project_name(parent_dirpath) - -- Check for blacklist - local is_blacklisted = #self.options.blacklist > 0 and self:check_blacklist(buffer, parent_dirpath, project_path) - if is_blacklisted then - self.last_activity.file = buffer - self.log:debug("Either project or directory name is blacklisted, skipping...") - self:cancel() - return - end + -- Check for blacklist + local is_blacklisted = #self.options.blacklist > 0 and self:check_blacklist(buffer, parent_dirpath, project_path) + if is_blacklisted then + self.last_activity.file = buffer + self.log:debug("Either project or directory name is blacklisted, skipping...") + self:cancel() + return + end - local activity_set_at = os.time() - -- If we shouldn't debounce and we trigger an activity, keep this value the same. - -- Otherwise set it to the current time. - local relative_activity_set_at = should_debounce and self.last_activity.relative_set_at or os.time() + local activity_set_at = os.time() + -- If we shouldn't debounce and we trigger an activity, keep this value the same. + -- Otherwise set it to the current time. + local relative_activity_set_at = should_debounce and self.last_activity.relative_set_at or os.time() - self.log:debug(string.format("Setting activity for %s...", buffer and #buffer > 0 and buffer or "unnamed buffer")) + self.log:debug(string.format("Setting activity for %s...", buffer and #buffer > 0 and buffer or "unnamed buffer")) - -- Determine image text and asset key - local name = filename - local asset_key = "code" - local description = filename - local file_asset = self.options.file_assets[filename] or self.options.file_assets[extension] - if file_asset then - name, asset_key, description = unpack(file_asset) - self.log:debug(string.format("Using file asset: %s", vim.inspect(file_asset))) - end + -- Determine image text and asset key + local name = filename + local asset_key = "code" + local description = filename + local file_asset = self.options.file_assets[filename] or self.options.file_assets[extension] + if file_asset then + name, asset_key, description = unpack(file_asset) + self.log:debug(string.format("Using file asset: %s", vim.inspect(file_asset))) + end - -- Construct activity asset information - local file_text = description or name - local neovim_image_text = self.options.neovim_image_text - local use_file_as_main_image = self.options.main_image == "file" - local use_neovim_as_main_image = self.options.main_image == "neovim" - local assets = { - large_image = use_file_as_main_image and asset_key or use_neovim_as_main_image - and "neovim" or self.options.main_image, - large_text = use_file_as_main_image and file_text or neovim_image_text, - small_image = use_file_as_main_image and "neovim" or asset_key, - small_text = use_file_as_main_image and neovim_image_text or file_text, - } + -- Construct activity asset information + local file_text = description or name + local neovim_image_text = self.options.neovim_image_text + local use_file_as_main_image = self.options.main_image == "file" + local use_neovim_as_main_image = self.options.main_image == "neovim" + local assets = { + large_image = use_file_as_main_image and asset_key + or use_neovim_as_main_image and "neovim" + or self.options.main_image, + large_text = use_file_as_main_image and file_text or neovim_image_text, + small_image = use_file_as_main_image and "neovim" or asset_key, + small_text = use_file_as_main_image and neovim_image_text or file_text, + } - local activity = { - state = status_text, - assets = assets, - timestamps = self.options.show_time == 1 and { - start = relative_activity_set_at, - } or nil, - } + local activity = { + state = status_text, + assets = assets, + timestamps = self.options.show_time == 1 and { + start = relative_activity_set_at, + } or nil, + } - -- Add button that links to the git workspace remote origin url - if self.options.buttons ~= 0 then - local buttons = self:get_buttons(buffer, parent_dirpath) - if buttons then - self.log:debug(string.format("Attaching buttons to activity: %s", vim.inspect(buttons))) - activity.buttons = buttons - end - end + -- Add button that links to the git workspace remote origin url + if self.options.buttons ~= 0 then + local buttons = self:get_buttons(buffer, parent_dirpath) + if buttons then + self.log:debug(string.format("Attaching buttons to activity: %s", vim.inspect(buttons))) + activity.buttons = buttons + end + end - -- Get the current line number and line count if the user has set the enable_line_number option - if self.options.enable_line_number == 1 then - self.log:debug("Getting line number for current buffer...") + -- Get the current line number and line count if the user has set the enable_line_number option + if self.options.enable_line_number == 1 then + self.log:debug("Getting line number for current buffer...") - local line_number = vim.api.nvim_win_get_cursor(0)[1] - local line_count = vim.api.nvim_buf_line_count(0) - local line_number_text = self:format_status_text("line_number", line_number, line_count) + local line_number = vim.api.nvim_win_get_cursor(0)[1] + local line_count = vim.api.nvim_buf_line_count(0) + local line_number_text = self:format_status_text("line_number", line_number, line_count) - activity.details = line_number_text + activity.details = line_number_text - self.workspace = nil - self.last_activity = { - id = self.id, - file = buffer, - set_at = activity_set_at, - relative_set_at = relative_activity_set_at, - workspace = nil, - } - else - -- Include project details if available and if the user hasn't set the enable_line_number option - if project_name then - self.log:debug(string.format("Detected project: %s", project_name)) + self.workspace = nil + self.last_activity = { + id = self.id, + file = buffer, + set_at = activity_set_at, + relative_set_at = relative_activity_set_at, + workspace = nil, + } + else + -- Include project details if available and if the user hasn't set the enable_line_number option + if project_name then + self.log:debug(string.format("Detected project: %s", project_name)) - activity.details = self:format_status_text("workspace", project_name, buffer) + activity.details = self:format_status_text("workspace", project_name, buffer) - self.workspace = project_path - self.last_activity = { - id = self.id, - file = buffer, - set_at = activity_set_at, - relative_set_at = relative_activity_set_at, - workspace = project_path, - } + self.workspace = project_path + self.last_activity = { + id = self.id, + file = buffer, + set_at = activity_set_at, + relative_set_at = relative_activity_set_at, + workspace = project_path, + } - if self.workspaces[project_path] then - self.workspaces[project_path].updated_at = activity_set_at - activity.timestamps = self.options.show_time == 1 - and { - start = self.workspaces[project_path].started_at, - } - or nil - else - self.workspaces[project_path] = { - started_at = activity_set_at, - updated_at = activity_set_at, - } - end - else - self.log:debug("No project detected") + if self.workspaces[project_path] then + self.workspaces[project_path].updated_at = activity_set_at + activity.timestamps = self.options.show_time == 1 + and { + start = self.workspaces[project_path].started_at, + } + or nil + else + self.workspaces[project_path] = { + started_at = activity_set_at, + updated_at = activity_set_at, + } + end + else + self.log:debug("No project detected") - self.workspace = nil - self.last_activity = { - id = self.id, - file = buffer, - set_at = activity_set_at, - relative_set_at = relative_activity_set_at, - workspace = nil, - } + self.workspace = nil + self.last_activity = { + id = self.id, + file = buffer, + set_at = activity_set_at, + relative_set_at = relative_activity_set_at, + workspace = nil, + } - -- When no project is detected, set custom workspace text if: - -- * The custom function returns custom workspace text - -- * The configured workspace text does not contain a directive - -- (can't use the `format_status_text` method here) - local workspace_text = self.options.workspace_text - if type(workspace_text) == "function" then - local custom_workspace_text = workspace_text(nil, buffer) - if custom_workspace_text then - activity.details = custom_workspace_text - end - elseif not workspace_text:find("%s") then - activity.details = workspace_text - end - end - end + -- When no project is detected, set custom workspace text if: + -- * The custom function returns custom workspace text + -- * The configured workspace text does not contain a directive + -- (can't use the `format_status_text` method here) + local workspace_text = self.options.workspace_text + if type(workspace_text) == "function" then + local custom_workspace_text = workspace_text(nil, buffer) + if custom_workspace_text then + activity.details = custom_workspace_text + end + elseif not workspace_text:find("%s") then + activity.details = workspace_text + end + end + end - -- Sync activity to all peers - self.log:debug("Sync activity to all peers...") - self:sync_self_activity() + -- Sync activity to all peers + self.log:debug("Sync activity to all peers...") + self:sync_self_activity() - self.log:debug("Setting Discord activity...") - self.discord:set_activity(activity, function(err) - if err then - self.log:error(string.format("Failed to set activity in Discord: %s", err)) - return - end + self.log:debug("Setting Discord activity...") + self.discord:set_activity(activity, function(err) + if err then + self.log:error(string.format("Failed to set activity in Discord: %s", err)) + return + end - self.log:info(string.format("Set activity in Discord for %s", filename)) - end) + self.log:info(string.format("Set activity in Discord for %s", filename)) + end) end -- Update Rich Presence for the current or provided vim buffer for an authorized connection Presence.update = Presence.discord_event(function(self, buffer, should_debounce) - -- Default update to not debounce by default - if should_debounce == nil then - should_debounce = false - end + -- Default update to not debounce by default + if should_debounce == nil then + should_debounce = false + end - -- Debounce Rich Presence updates (default to 10 seconds): - -- https://discord.com/developers/docs/rich-presence/how-to#updating-presence - local last_updated_at = self.last_activity.set_at - local debounce_timeout = self.options.debounce_timeout - local should_skip = should_debounce - and debounce_timeout - and last_updated_at - and os.time() - last_updated_at <= debounce_timeout + -- Debounce Rich Presence updates (default to 10 seconds): + -- https://discord.com/developers/docs/rich-presence/how-to#updating-presence + local last_updated_at = self.last_activity.set_at + local debounce_timeout = self.options.debounce_timeout + local should_skip = should_debounce + and debounce_timeout + and last_updated_at + and os.time() - last_updated_at <= debounce_timeout - if should_skip then - local message_fmt = "Last activity sent was within %d seconds ago, skipping..." - self.log:debug(string.format(message_fmt, debounce_timeout)) - return - end + if should_skip then + local message_fmt = "Last activity sent was within %d seconds ago, skipping..." + self.log:debug(string.format(message_fmt, debounce_timeout)) + return + end - if buffer then - self:update_for_buffer(buffer, should_debounce) - else - vim.schedule(function() - self:update_for_buffer(self.get_current_buffer(), should_debounce) - end) - end + if buffer then + self:update_for_buffer(buffer, should_debounce) + else + vim.schedule(function() + self:update_for_buffer(self.get_current_buffer(), should_debounce) + end) + end end) -------------------------------------------------- @@ -999,190 +1000,190 @@ end) -- Register some remote peer function Presence:register_peer(id, socket) - self.log:debug(string.format("Registering peer %s...", id)) + self.log:debug(string.format("Registering peer %s...", id)) - self.peers[id] = { - socket = socket, - workspace = nil, - } + self.peers[id] = { + socket = socket, + workspace = nil, + } - self.log:info(string.format("Registered peer %s", id)) + self.log:info(string.format("Registered peer %s", id)) end -- Unregister some remote peer function Presence:unregister_peer(id, peer) - self.log:debug(string.format("Unregistering peer %s... %s", id, vim.inspect(peer))) + self.log:debug(string.format("Unregistering peer %s... %s", id, vim.inspect(peer))) - -- Remove workspace if no other peers share the same workspace - -- Initialize to remove if the workspace differs from the local workspace, check peers below - local should_remove_workspace = peer.workspace ~= self.workspace + -- Remove workspace if no other peers share the same workspace + -- Initialize to remove if the workspace differs from the local workspace, check peers below + local should_remove_workspace = peer.workspace ~= self.workspace - local peers = {} - for peer_id, peer_data in pairs(self.peers) do - -- Omit peer from peers list - if peer_id ~= id then - peers[peer_id] = peer_data + local peers = {} + for peer_id, peer_data in pairs(self.peers) do + -- Omit peer from peers list + if peer_id ~= id then + peers[peer_id] = peer_data - -- Should not remove workspace if another peer shares the workspace - if should_remove_workspace and peer.workspace == peer_data.workspace then - should_remove_workspace = false - end - end - end + -- Should not remove workspace if another peer shares the workspace + if should_remove_workspace and peer.workspace == peer_data.workspace then + should_remove_workspace = false + end + end + end - self.peers = peers + self.peers = peers - -- Update workspaces if necessary - local workspaces = {} - if should_remove_workspace then - self.log:debug(string.format("Should remove workspace %s", peer.workspace)) - for workspace, data in pairs(self.workspaces) do - if workspace ~= peer.workspace then - workspaces[workspace] = data - end - end + -- Update workspaces if necessary + local workspaces = {} + if should_remove_workspace then + self.log:debug(string.format("Should remove workspace %s", peer.workspace)) + for workspace, data in pairs(self.workspaces) do + if workspace ~= peer.workspace then + workspaces[workspace] = data + end + end - self.workspaces = workspaces - end + self.workspaces = workspaces + end - self.log:info(string.format("Unregistered peer %s", id)) + self.log:info(string.format("Unregistered peer %s", id)) end -- Unregister some remote peer and set activity function Presence:unregister_peer_and_set_activity(id, peer) - self:unregister_peer(id, peer) - self:update() + self:unregister_peer(id, peer) + self:update() end -- Register a remote peer and sync its data function Presence:register_and_sync_peer(id, socket) - self:register_peer(id, socket) + self:register_peer(id, socket) - self.log:debug("Syncing data with newly registered peer...") + self.log:debug("Syncing data with newly registered peer...") - -- Initialize the remote peer's list including self - local peers = { - [self.id] = { - socket = self.socket, - workspace = self.workspace, - }, - } - for peer_id, peer in pairs(self.peers) do - if peer_id ~= id then - peers[peer_id] = peer - end - end + -- Initialize the remote peer's list including self + local peers = { + [self.id] = { + socket = self.socket, + workspace = self.workspace, + }, + } + for peer_id, peer in pairs(self.peers) do + if peer_id ~= id then + peers[peer_id] = peer + end + end - self:call_remote_method( - socket, - "sync_self", - { { - last_activity = self.last_activity, - peers = peers, - workspaces = self.workspaces, - } } - ) + self:call_remote_method( + socket, + "sync_self", + { { + last_activity = self.last_activity, + peers = peers, + workspaces = self.workspaces, + } } + ) end -- Register self to any remote Neovim instances -- Simply emits to all nvim sockets as we have not yet been synced with peer list function Presence:register_self() - self:get_nvim_socket_paths(function(sockets) - if #sockets == 0 then - self.log:debug("No other remote nvim instances") - return - end + self:get_nvim_socket_paths(function(sockets) + if #sockets == 0 then + self.log:debug("No other remote nvim instances") + return + end - self.log:debug(string.format("Registering as a new peer to %d instance(s)...", #sockets)) + self.log:debug(string.format("Registering as a new peer to %d instance(s)...", #sockets)) - -- Register and sync state with one of the sockets - self:call_remote_method(sockets[1], "register_and_sync_peer", { self.id, self.socket }) + -- Register and sync state with one of the sockets + self:call_remote_method(sockets[1], "register_and_sync_peer", { self.id, self.socket }) - if #sockets == 1 then - return - end + if #sockets == 1 then + return + end - for i = 2, #sockets do - self:call_remote_method(sockets[i], "register_peer", { self.id, self.socket }) - end - end) + for i = 2, #sockets do + self:call_remote_method(sockets[i], "register_peer", { self.id, self.socket }) + end + end) end -- Unregister self to all peers function Presence:unregister_self() - local self_as_peer = { - socket = self.socket, - workspace = self.workspace, - } + local self_as_peer = { + socket = self.socket, + workspace = self.workspace, + } - local i = 1 - for id, peer in pairs(self.peers) do - if self.options.auto_update and i == 1 then - self.log:debug(string.format("Unregistering self and setting activity for peer %s...", id)) - self:call_remote_method(peer.socket, "unregister_peer_and_set_activity", { self.id, self_as_peer }) - else - self.log:debug(string.format("Unregistering self to peer %s...", id)) - self:call_remote_method(peer.socket, "unregister_peer", { self.id, self_as_peer }) - end - i = i + 1 - end + local i = 1 + for id, peer in pairs(self.peers) do + if self.options.auto_update and i == 1 then + self.log:debug(string.format("Unregistering self and setting activity for peer %s...", id)) + self:call_remote_method(peer.socket, "unregister_peer_and_set_activity", { self.id, self_as_peer }) + else + self.log:debug(string.format("Unregistering self to peer %s...", id)) + self:call_remote_method(peer.socket, "unregister_peer", { self.id, self_as_peer }) + end + i = i + 1 + end end -- Sync self with data from a remote peer function Presence:sync_self(data) - self.log:debug(string.format("Syncing data from remote peer...", vim.inspect(data))) + self.log:debug(string.format("Syncing data from remote peer...", vim.inspect(data))) - for key, value in pairs(data) do - self[key] = value - end + for key, value in pairs(data) do + self[key] = value + end - self.log:info("Synced runtime data from remote peer") + self.log:info("Synced runtime data from remote peer") end -- Sync activity set by self to all peers function Presence:sync_self_activity() - local self_as_peer = { - socket = self.socket, - workspace = self.workspace, - } + local self_as_peer = { + socket = self.socket, + workspace = self.workspace, + } - for id, peer in pairs(self.peers) do - self.log:debug(string.format("Syncing activity to peer %s...", id)) + for id, peer in pairs(self.peers) do + self.log:debug(string.format("Syncing activity to peer %s...", id)) - local peers = { [self.id] = self_as_peer } - for peer_id, peer_data in pairs(self.peers) do - if peer_id ~= id then - peers[peer_id] = { - socket = peer_data.socket, - workspace = peer_data.workspace, - } - end - end + local peers = { [self.id] = self_as_peer } + for peer_id, peer_data in pairs(self.peers) do + if peer_id ~= id then + peers[peer_id] = { + socket = peer_data.socket, + workspace = peer_data.workspace, + } + end + end - self:call_remote_method( - peer.socket, - "sync_peer_activity", - { { - last_activity = self.last_activity, - peers = peers, - workspaces = self.workspaces, - } } - ) - end + self:call_remote_method( + peer.socket, + "sync_peer_activity", + { { + last_activity = self.last_activity, + peers = peers, + workspaces = self.workspaces, + } } + ) + end end -- Sync activity set by peer function Presence:sync_peer_activity(data) - self.log:debug(string.format("Syncing peer activity %s...", vim.inspect(data))) - self:cancel() - self:sync_self(data) + self.log:debug(string.format("Syncing peer activity %s...", vim.inspect(data))) + self:cancel() + self:sync_self(data) end function Presence:stop() - self.log:debug("Disconnecting from Discord...") - self.discord:disconnect(function() - self.log:info("Disconnected from Discord") - end) + self.log:debug("Disconnecting from Discord...") + self.discord:disconnect(function() + self.log:info("Disconnected from Discord") + end) end -------------------------------------------------- @@ -1191,98 +1192,98 @@ end -- FocusGained events force-update the presence for the current buffer unless it's a quickfix window function Presence:handle_focus_gained() - self.log:debug("Handling FocusGained event...") + self.log:debug("Handling FocusGained event...") - -- Skip a potentially extraneous update call on initial startup if tmux is being used - -- (See https://github.com/neovim/neovim/issues/14572) - if next(self.last_activity) == nil and os.getenv("TMUX") then - self.log:debug("Skipping presence update for FocusGained event triggered by tmux...") - return - end + -- Skip a potentially extraneous update call on initial startup if tmux is being used + -- (See https://github.com/neovim/neovim/issues/14572) + if next(self.last_activity) == nil and os.getenv("TMUX") then + self.log:debug("Skipping presence update for FocusGained event triggered by tmux...") + return + end - if vim.bo.filetype == "qf" then - self.log:debug("Skipping presence update for quickfix window...") - return - end + if vim.bo.filetype == "qf" then + self.log:debug("Skipping presence update for quickfix window...") + return + end - self:update() + self:update() end -- TextChanged events debounce current buffer presence updates function Presence:handle_text_changed() - self.log:debug("Handling TextChanged event...") - self:update(nil, true) + self.log:debug("Handling TextChanged event...") + self:update(nil, true) end -- VimLeavePre events unregister the leaving instance to all peers and sets activity for the first peer function Presence:handle_vim_leave_pre() - self.log:debug("Handling VimLeavePre event...") - self:unregister_self() - self:cancel() + self.log:debug("Handling VimLeavePre event...") + self:unregister_self() + self:cancel() end -- WinEnter events force-update the current buffer presence unless it's a quickfix window function Presence:handle_win_enter() - self.log:debug("Handling WinEnter event...") + self.log:debug("Handling WinEnter event...") - vim.schedule(function() - if vim.bo.filetype == "qf" then - self.log:debug("Skipping presence update for quickfix window...") - return - end + vim.schedule(function() + if vim.bo.filetype == "qf" then + self.log:debug("Skipping presence update for quickfix window...") + return + end - self:update() - end) + self:update() + end) end -- WinLeave events cancel the current buffer presence function Presence:handle_win_leave() - self.log:debug("Handling WinLeave event...") + self.log:debug("Handling WinLeave event...") - local current_window = vim.api.nvim_get_current_win() + local current_window = vim.api.nvim_get_current_win() - vim.schedule(function() - -- Avoid canceling presence when switching to a quickfix window - if vim.bo.filetype == "qf" then - self.log:debug("Not canceling presence due to switching to quickfix window...") - return - end + vim.schedule(function() + -- Avoid canceling presence when switching to a quickfix window + if vim.bo.filetype == "qf" then + self.log:debug("Not canceling presence due to switching to quickfix window...") + return + end - -- Avoid canceling presence when switching between windows - if current_window ~= vim.api.nvim_get_current_win() then - self.log:debug("Not canceling presence due to switching to a window within the same instance...") - return - end + -- Avoid canceling presence when switching between windows + if current_window ~= vim.api.nvim_get_current_win() then + self.log:debug("Not canceling presence due to switching to a window within the same instance...") + return + end - self.log:debug("Canceling presence due to leaving window...") - self:cancel() - end) + self.log:debug("Canceling presence due to leaving window...") + self:cancel() + end) end -- BufEnter events force-update the presence for the current buffer unless it's a quickfix window function Presence:handle_buf_enter() - self.log:debug("Handling BufEnter event...") + self.log:debug("Handling BufEnter event...") - if vim.bo.filetype == "qf" then - self.log:debug("Skipping presence update for quickfix window...") - return - end + if vim.bo.filetype == "qf" then + self.log:debug("Skipping presence update for quickfix window...") + return + end - self:update() + self:update() end -- BufAdd events force-update the presence for the current buffer unless it's a quickfix window function Presence:handle_buf_add() - self.log:debug("Handling BufAdd event...") + self.log:debug("Handling BufAdd event...") - vim.schedule(function() - if vim.bo.filetype == "qf" then - self.log:debug("Skipping presence update for quickfix window...") - return - end + vim.schedule(function() + if vim.bo.filetype == "qf" then + self.log:debug("Skipping presence update for quickfix window...") + return + end - self:update() - end) + self:update() + end) end return Presence diff --git a/lua/presence/plugin_managers.lua b/lua/presence/plugin_managers.lua index 3789ba3..7b92a43 100644 --- a/lua/presence/plugin_managers.lua +++ b/lua/presence/plugin_managers.lua @@ -1,5 +1,5 @@ -- Different plugin manager names return { - ["packer"] = "packer", - ["vim-plug"] = "vim-plug", + ["packer"] = "packer", + ["vim-plug"] = "vim-plug", }