Breakpoints and Stepping Widget?

Does anyone have a widget for creating breakpoints and stepping through code? I tried toffer’s utility but it depends on setfenv(), which is apparently no longer available.

This would be a fabulous feature to have in Codea 3.0!

@SolaFide - I agree, Lua does have some debug code which may be present but I don’t think is used. A breakpoint mechanism would be great for trapping errors mid run and stopping infinite loops. I think this is being considered at the moment.

For tracking loops, you can use the print(message) feature. For break points there’s assert(false, message), but I remember I’ve talked to either Simeon or John, and a debugging feature is planned, not in the nearest future however.

@SolaFide As @Anatoly said, print statement can be use but you have to be careful where they’re placed. If they’re in a function that gets called frequently then your code will slow down and eventually crash and won’t be of any help. Another thing you can use is saveGlobalData(key,value). You can use different key values for different places in your code and save the value. After you stop your program or it crashes, you can use a separate program to read the different keys to see what the last values were. So far the print statements have worked for me.

@SolaFide setfenv is no longer an available global function, but you can implement your own.

-- https://leafo.net/guides/setfenv-in-lua52-and-above.html
local function setfenv(fn, env)
  local i = 1
  while true do
    local name = debug.getupvalue(fn, i)
    if name == "_ENV" then
      debug.upvaluejoin(fn, i, (function()
        return env
      end), 1)
      break
    elseif not name then
      break
    end

    i = i + 1
  end

  return fn
end

For variable tracking I created this class, VariTrack. I create a global instance and functions can post messages to it to be drawn every frame.

@HyroVitalyProtago At your suggestion, I tried defining setfenv() that way. However, the debug functions it references are not exposed either. Maybe I can track those down as well.

VariTrack = class()

function VariTrack:init()
    self.messages = {}
end

function VariTrack:addMessage(str)
    table.insert(self.messages, str)
end

function VariTrack:draw()
    if #self.messages == 0 then 
        --self:addMessage("nada")
        return
    end
    
    pushMatrix()
    resetMatrix()
    pushStyle()
    
    textMode(CORNER)
    fill(124, 39, 242, 255)
    font("Didot-Bold")
    fontSize(16)
    
    for i, s in ipairs(self.messages) do
        text(s, 40, 20 * i)
    end
    
    while #self.messages > 0 do
        table.remove(self.messages)
    end
    
    popStyle()
    popMatrix()
end

function VariTrack:touched(touch)

end

@SolaFide When you post code, put 3 ~ on a line before and after your code so it formats correctly. I added them to your code above.

@SolaFide Your while loop to clear the table can be replaced with just redefining the table.

Replace this

while #self.messages > 0 do
        table.remove(self.messages)
end

with this

self.messages={}

But hey!

Since we’re talking about variable tracking, why has no one mentioned about parameters?

lua = 55
parameter.watch(‘ lua ’)

Pay attention, that’s ’lua’, not just lua.

@Anatoly parameter.watch doesn’t work in some situations. If you’re in a for loop, parameter.watch isn’t updated until the for loop is finished. And if you’re doing a lot of processing in the draw function, parameter.watch isn’t updated until the draw function finishes.

@dave1707 True. Didn’t thought of that situation. It’s possible however to create a temporary array x = {} and to insert all he changes there, and then print them as one message.

This fixes both the time problem and the initial problem (debugging).

What do you think?

@Anatoly That’s what @SolaFide did in his VariTrack class above. He created a table self.messages that can be updated with VariTrack:addMessage and then displayed with VariTrack:draw(). For me, if done right, print statements gives enough information to figure out what’s going wrong.

@SolaFide Sorry, I wasn’t with my iPad, so, this is something that’s works for me :

    local function findenv(f)
        local level = 1
        repeat
            local name, value = debug.getupvalue(f, level)
            if name == '_ENV' then
                return level, value
            end
            level = level + 1
        until name == nil
        return nil
    end
    function getfenv(f)
        return(select(2, findenv(f)) or _G)
    end
    function setfenv(f, t)
        local level = findenv(f)
        if level then
            debug.setupvalue(f, level, t)
        end
        return f
    end