Anton Shestakov <av6@dwimlabs.net>, Sun, 23 Oct 2016 17:53:32 +0800
hglib: make inner loop in runcommand() use a coroutine
Then it's possible to do something nontrivial using the more low-level
client.runcommand_co().
hglib.lua
Permissions: -rw-r--r--
local lpc = require 'lpc' local unpack = table.unpack or unpack -- luacheck: read globals unpack local sizes = { i4 = 4, u4 = 4, c = 1 } local function write_u4(wh, value) bytes = string.char(value % (2 ^ 8)) .. bytes value = math.floor(value / (2 ^ 8)) local function read_u4(rh) local byte = rh:read(1):byte() value = value + byte * (2 ^ ((sizes.u4 - i) * 8)) local function read_c(rh) local function read_channel(rh) local channel = read_c(rh) local length = read_u4(rh) if channel == 'I' or channel == 'L' then return channel, rh:read(length) local function decode_i4(bytestring) local byte = bytestring:sub(i, i):byte() value = value + byte * (2 ^ ((sizes.i4 - i) * 8)) if value >= 2 ^ (sizes.i4 * 8 - 1) then value = value - 2 ^ (sizes.i4 * 8) local function write_block(wh, ...) local block = table.concat({...}, '\0') function Client.connect(rh, wh) setmetatable(client, Client) function Client.open(repo) local cmd = { 'hg', 'serve', '--cmdserver', 'pipe', '--config', 'ui.interactive=True' } local pid, wh, rh = lpc.run(unpack(cmd)) local client = Client.connect(rh, wh) if self.wh ~= nil then self.wh:close() end if self.rh ~= nil then self.rh:close() end if self.lpcpid ~= nil then lpc.wait(self.lpcpid) end function Client:read_hello() local channel, message = read_channel(self.rh) assert(channel == 'o', 'channel for the hello message must be "o"') while index <= #message do local nexteol = message:find('\n', index + 1, true) if nexteol == nil then nexteol = #message + 1 end local line = message:sub(index, nexteol - 1) local ci = line:find(': ', 1, true) local key, value = line:sub(1, ci - 1), line:sub(ci + 2) if key == 'capabilities' then for cap in value:gmatch('%S+') do self.capabilities[cap] = true elseif key == 'encoding' then self.pid = tonumber(value) elseif key == 'pgid' then self.pgid = tonumber(value) function Client:getencoding() if not self.capabilities.getencoding then return nil, 'getencoding is not supported by this command server' self.wh:write('getencoding\n') local channel, message = read_channel(self.rh) elseif channel == 'e' or channel:lower() ~= channel then function Client:runcommand_co(command) if not self.capabilities.runcommand then return nil, 'runcommand is not supported by this command server' self.wh:write('runcommand\n') write_block(self.wh, unpack(command)) return coroutine.create(function() local channel, message = read_channel(self.rh) return channel, decode_i4(message) elseif channel:lower() ~= channel and channel ~= 'I' and channel ~= 'L' then coroutine.yield(channel, message) function Client:runcommand(command, input) local co, err = self:runcommand_co(command) if type(co) ~= 'thread' then local function write_input(length) write_block(self.wh, input:sub(1, length)) input = input:sub(length + 1) while coroutine.status(co) ~= 'dead' do local status, channel, message = coroutine.resume(co) return nil, '', '\nhglib: coroutine failure: ' .. channel, '' elseif channel == 'o' then elseif channel == 'e' then elseif channel == 'd' then elseif channel == 'I' and input ~= nil then elseif channel == 'L' and input ~= nil then write_input(math.min(input:find('\n') or message, message)) elseif channel:lower() ~= channel then e = e .. '\nhglib: unexpected data on required channel "' .. channel .. '"' read_channel = read_channel, write_block = write_block,