Dynamically assign bangs in LSLua Thread last updated on 2006-12-27 15:36:52

Posted by member 12025 on 2006-12-26 18:09:13

Yup, another stupid LSLua trick. LSLua only allows bangs to be set up when it loads files. This is just a little (well, if it weren't for 250 bangs at the end!) module to give a bang to a Lua function. The top comment should give enough of how it could be used. You probably wouldn't want to allocate too many more than you would use, due to Lua's problems with too many bangs defined, and I really can't think of a way to use more than 50, really (but the amount of them does not change the workings in any way, so it's made for an impractical maximum).

The basic idea is that an xModule (mainly xLabel) event could be set to fire a Lua function. By returning the bang name, the action could be easily set up and then that name (or the function passed) used to return it to the pool.

P.S. While LS.net eats them upon an edit, works great for tabs.

--[[
    rentbangs 1.1: to do runtime dynamic bang command assignment and release.
    by cerbie
    
    Use, once it is in a script module folder, and named rentbangs.lua:
        require "rentbangs"
    This must be done as part of loading LSLua, occuring *before* lslua.init ()
    is run! If so, it will ad as many bangs as it is set for here, up to 250.
    
    rentbangs.rent (myfunc):
        You pass a function to this, and it will assign that function to a bang
        command that is available, returning the bang command name.
    
    rentbangs.free (bang):
        You pass either the bang's name or its function, and this will return
        the original function, and free up that bang command.
    
    rentbangs.countused ():
        Returns the number of bangs currently in use. Left over from initial
        creation, it still works, and may end up useful, somewhow. If not, it's
        only wasting 7 lines, and certainly uses less RAM than the list of bang
        commands does.
    
    example:
        foo = rentbangs.rent (lslua.exec)
        print foo: "!mybang3"
        run "!mybang3 !alert $Win2000$": a lslua.message_box that says "true".
        do: bar = rentbangs.free (foo)
        or do: bar = rentbangs.free ("!mybang3")
        print tostring (bar == lslua.exec): "true"
    
    Just below this comment is the count of bangs. Somewhere in the mid 200s
    things get weird. As such, 250 is the max.
    
    Changes:
    1.1: from THC4K; package.seeall makes a small version work.
]]

require "evar"

__rentbangs__count = 100;
__rentbangs__name = "forleasecall";
__rentbangs__donothing = function (a) return a end

module ("rentbangs", package.seeall)

bangs = {}

gpre = '_G [ "bang_'; -- now sort of pointless
gsuf = '" ] = __rentbangs__donothing'; -- now sort of pointless

__makelist = function ()
    name = __rentbangs__name
    count = __rentbangs__count
    
    bangs = {}
    for i=1,count do -- no checking for existence is done anymore
        bangs [ "!"..name..i ] = false
    end
end

rent = function (assignto)
    if type (assignto) == "function" then
        local gotone = false
        for k,v in pairs (bangs) do -- slower than using multiple lists
            if not v then
                gotone = k
                bangs [ gotone ] = assignto
                _G [ "bang_".. gotone:sub (2) ] = assignto
                break
            end
        end
        
        if gotone then
            return gotone
        else
            box ("rentbang.rent: no more bangs to use!\nSet __rentbangs__count higher!")
        end
    else
        box ("rentbang.rent: bad argument:\n"..tostring(bang))
        return false
    end
end

free = function (bang) -- may be function or name
    if type (bang) == "function" then
        local found = false
        
        for k,v in pairs (bangs) do
            if v == bang then
                bangs [ k ] = false
                _G [ "bang_"..k:sub (2) ] = _G.__rentbangs__donothing
                return v
            end
        end

        if not found then
            box ("rentbangs.free: "..tostring (bang).." not found in use!")
        end
    elseif type (bang) == "string" then
        local found = bangs [ bang ]
        
        if found then
            bangs [ bang ] = false
            _G [ "bang_"..bang:sub (2) ] = _G.__rentbangs__donothing
            return found
        else
            box ("rentbangs.free: "..bang.." not found in use!\n")
        end
    end
    return false
end

countused = function ()
    local c = 0
    for k,v in pairs (_M.bangs) do
        if v then c = c + 1    end
    end
    return c
end

for i=1,__rentbangs__count do
    _G [ "bang_"..__rentbangs__name..i ] = __rentbangs__donothing
end

__makelist ()

Posted by member 12025 on 2006-12-26 18:25:48 link

P.P.S.: LS.net ate a few \\\" marks, too. I don't think it will hurt anything, though (I used single quotes around them and just \\\" to keep things from appearing ambiguous).

Posted by member 93947 on 2006-12-26 22:30:19 link

lol cerbie .. your coding was always strange but this is a new high.
http://www.lua.org/manual/5.1/manual.html#2.4.5 ... really.

for k=1, 250 do _G["bang_forleasecall"..k] =function () end end

Posted by member 12025 on 2006-12-27 00:35:48 link

The for loop doesn't work. It acts just as if I has done it after LSLua has loaded (it doesn't monitor _G, that or it only does so before lslua.init () runs). I made the ifs one-liners, though.

Note that such a for loop seems to work just find outside of the module file itself. I think it has to do with the module spec and trying to mess with _G. FI, your loop as you have it works, where mine, using the variables as stated, does not. But, I don't want to waste so many functions, nor make there be multiple "name" or "count" variables (the top one is deprecated).

To be more specific, for i=1,250 do works, but for i=1,_G.__rentbangs__count do does not.

If I do is as for i=1,__rentbangs__count do, with no _G, it gives an error that the limit must be a number (it is is I try to access it later--outside the module).

If I use the module's "count", or set the __ one inside it, I get no error, but again, it does not work. So I can have the many lines, or have it check _G, or do the bang definitions outside the module, based on what I've tried so far. I could also iterate through _G with pairs(), but I just don't like that solution in general, as it does mean going through all of _G, for all practical purposes.

Posted by member 93947 on 2006-12-27 12:44:28 link

First, here is a WTF for your post. You have funny ideas.
Now, write local __rentbangs__count = 50 and use a loop ...
(Or remove all these "local blah=blah" lines, all the _G and _M strangeness, and use package.seeall because that is what you are simulation over all these lines ...)

Posted by member 12025 on 2006-12-27 15:35:54 link

_G and _M strangeness: hey, I'm just following some suggestions by either doy ot tnl a good while back that worked :).

I'm not simulating package.seeall with the locals, but simulating doing a for loop using setfenv on the functions over the whole module; though it is an ideal solution for this one, as the isolation doesn't offer any benefits for working with/on it. But, I'd basically forgotten package.seeall, by this point :).