I'm trying write a neovim plugin using lua, when checking if a variable exists, lua throws an error like: Undefined variable: g:my_var
Method 1:
local function open_bmax_term()
if (vim.api.nvim_eval("g:my_var")) then
print('has the last buff')
else
print('has no the last buff')
end
end
Method 2:
local function open_bmax_term()
if (vim.api.nvim_get_var("my_var")) then
print('has the last buff')
else
print('has no the last buff')
end
end
this is a similar function written in viml which does works: (this does not throw any error)
fun! OpenBmaxTerm()
if exists("g:my_var")
echo "has the last buff"
else
echo "has no the last buff"
endif
endfun
any idea how to get this working in lua? I tried wrapping the condition inside a pcall which had an effect like making it always truthy.
vim.api.nvim_eval("g:my_var") just evaluates a vimscript expression, so accessing a non-existent variable would error just like in vimscript. Have you tried vim.api.nvim_eval('exists("g:my_var")') instead?
Edit: Using vim.g as #andrewk suggested is probably the better solution as using the dedicated API is more elegant than evaluating strings of vim script.
You can use the global g: dictionary via vim.g to reference your variable:
if vim.g.my_var == nil then
print("g:my_var does not exist")
else
print("g:my_var was set to "..vim.g.my_var)
end
You can reference :h lua-vim-variables to see other global Vim dictionaries that are available as well!
When require is called in testt.lua which is one of two files the return is movee and movee.lua.
movee are for the most part a class to be required, but should be able to accept to be called direct with parameter.
movee.lua
local lib = {} --this is class array
function lib.moveAround( ... )
for i,direction in ipairs(arg) do
print(direction)
end
end
function lib.hello()
print("Hello water jump")
end
lib.moveAround(...)
return lib
testt.la
local move = require("movee")
Expected result is not to call lib.moveAround or print of file name when require is called.
Your expectations are incorrect. Lua, and most scripting languages for that matter, does not recognize much of a distinction between including a module and executing the Lua file which provides that module. Every function statement is a statement whose execution creates a function object. Until those statements are executed, those functions don't exist. Same goes for your local lib = {}. And so on.
Now, if you want to make a distinction between when a user tries to require your script as a module and when a user tries to execute your script on the command line (or via just loadfile or similar), then I would suggest doing the following.
Check the number of arguments the script was given. If no arguments were given, then your script was probably required, so don't do the stuff you don't want to do when the user requires your script:
local nargs = select("#", ...)
if(nargs > 0) then
lib.moveAround(...)
end
Solved by replacing
lib.moveAround(...)
with
local argument = {...}
if argument[1] ~= "movee" and argument[2] ~= "movee" then
lib.moveAround(...)
end
require("movee")
will execute the code within movee.lua
lib.moveAround(...)
is part of that code. Hence if you require "movee" you call lib.moveAround
If the expected result is not to call it, remove that line from your code or don't require that file.
I have something along the following lines in one of my spec files:
expect(my_instance).to receive(:my_function).with(arg: instance_of(String))
I want to be able to capture the actual value of arg in a variable I can use in the spec. Is there a way to do that? I checked the rspec docs but didn't find anything like that.
You could declare the variable, say captured_arg before the expect (or allow, if you don't want it to fail if my_instance does not receive my_function). Then you can collect the arguments in a block and set captured_arg within that block.
captured_arg = nil
expect(my_instance).to receive(:my_function) { |arg| captured_arg = arg }
Edit: (Keyword Arguments)
If you are using keyword arguments, just modify the script above slightly, using arg as the keyword argument you'd like to capture:
captured_arg = nil
expect(my_instance).to receive(:my_function) { |args| captured_arg = args[:arg] }
function OnChat(PlayerId, Type, Message)
if Message == "!usecarepackage" then
if ReadKS1 == "CP" or ReadKS2 == "CP" or ReadKS3 == "CP" then
InputConsole("msg %s has requested a care package!", Get_Player_Name_By_ID(pID))
local pos = Get_Position(Get_GameObj(pID))
pos:AssignY(pos:GetY()+1)
building = Create_Object("SignalFlare_Gold_Phys3", pos)
Attach_Script(building, "Test_Cinematic", "caredrop.txt")
if building == nil then
InputConsole("ppage %d Cannot spawn Care Package here.", pID)
elseif math.random(50, 64) == 50 then
Attach_Script(building, "z_Set_Team", Get_Team(pID))
Attach_Script(building, "M00_No_Falling_Damage_DME")
--Add in rest of numbers and corresponding streak, such as UAV if math.random = 50
end
elseif ReadKS1 ~= "CP" or ReadKS2 ~= "CP" or ReadKS3 ~= "CP" then
InputConsole("ppage %d You are not allowed to use that with your current streak selection", pID)
end
end
return 1
end
I know this is scruffy code, but I'm receiving a "Bad argument #2 to 'format' (number expected, got no value)". This relates to this piece of code which prefixes all other pieces:
function InputConsole(...)
Console_Input(string.format(unpack(arg)))
end
Finally, this is for the game Command and Conquer Renegade (should you wish to look up API etc). Any help in what I'm doing wrong would be much appreciated.
The likely problem with this code is the use of the deprecated arg feature in your function InputConsole(). Lua 5.0 used arg as a way to access the actual arguments to a function declared as variadic using the ... token in the argument list.
Edit: But likely doesn't mean correct.
The actual problem looks like the switch in idiom from PlayerId to pID. The former is the named parameter of the function OnChat(), while the latter is a global variable used in the function body without further initialization. Uninitialized globals are nil, so nil is passed to InputConsole() and the error message is telling you the truth.
Old answer that didn't solve it
Lua 5.1 deprecated that usage, and Lua 5.2 removed it entirely.
I'm not sure from the code fragments provided which version of Lua is actually used in this game, but the symptom is consistent with missing the automatically generated arg table.
I would write the function like this:
function InputConsole(...)
Console_Input(string.format(...))
end
But you could also add local arg = {...} as the first line of the function and get much the same effect as provided by Lua 5.0 at the expense of creating (and discarding) the temporary table. The differences are subtle, and mostly have to do with the treatment of nil in tables.
For clarity, I would prefer name the first argument, as it is not really optional.
function InputConsole(fmt, ...)
Console_Input(string.format(fmt, ...))
end
And if you can count on that argument being a string, that could be simplified further to
function InputConsole(fmt,...)
Console_Input(fmt:format(...))
end
If its stringiness is of concern, just say fmt = tostring(fmt) or possibly assert(type(fmt)=="string") before the call to Console_Input().
tl;dr: What design pattern allows you to split Lua code over multiple files that need to share some information without affecting the global table?
Background
It is considered bad form to create a library in Lua where requiring the library affects the global namespace:
--> somelib.lua <--
SomeLib = { ... }
--> usercode.lua <--
require 'somelib'
print(SomeLib) -- global key created == bad
Instead, it is considered a best practice to create a library that uses local variables and then returns them for the user to assign as they see fit:
--> somelib.lua <--
local SomeLib = { ... }
return SomeLib
--> usercode.lua <--
local theLib = require 'somelib' -- consumers name lib as they wish == good
The above pattern works fine when using a single file. However, this becomes considerably harder when you have multiple files that reference each other.
Concrete Example
How can you rewrite the following suite of files so that the assertions all pass? Ideally the rewrites will leave the same files on disk and responsibilities for each file. (Rewriting by merging all code into a single file is effective, but not helpful ;)
--> test_usage.lua <--
require 'master'
assert(MASTER.Simple)
assert(MASTER.simple)
assert(MASTER.Shared)
assert(MASTER.Shared.go1)
assert(MASTER.Shared.go2)
assert(MASTER.Simple.ref1()==MASTER.Multi1)
assert(pcall(MASTER.Simple.ref2))
assert(_G.MASTER == nil) -- Does not currently pass
--> master.lua <--
MASTER = {}
require 'simple'
require 'multi'
require 'shared1'
require 'shared2'
require 'shared3'
require 'reference'
--> simple.lua <--
MASTER.Simple = {}
function MASTER:simple() end
--> multi.lua <--
MASTER.Multi1 = {}
MASTER.Multi2 = {}
--> shared1.lua <--
MASTER.Shared = {}
--> shared2.lua <--
function MASTER.Shared:go1() end
--> shared3.lua <--
function MASTER.Shared:go2() end
--> reference.lua <--
function MASTER.Simple:ref1() return MASTER.Multi1 end
function MASTER.Simple:ref2() MASTER:simple() end
Failure: Setting the Environment
I thought to solve the problem by setting the environment to my master table with a self-reference. This does not work when calling functions like require however, as they change the environment back:
--> master.lua <--
foo = "original"
local MASTER = setmetatable({foo="captured"},{__index=_G})
MASTER.MASTER = MASTER
setfenv(1,MASTER)
require 'simple'
--> simple.lua <--
print(foo) --> "original"
MASTER.Simple = {} --> attempt to index global 'MASTER' (a nil value)
You are giving master.lua two responsibilities:
It defines the common module table
It imports all of the submodules
Instead you should create a separate module for (1) and import it in all of the submodules:
--> common.lua <--
return {}
--> master.lua <--
require 'simple'
require 'multi'
require 'shared1'
require 'shared2'
require 'shared3'
require 'reference'
return require'common' -- return the common table
--> simple.lua <--
local MASTER = require'common' -- import the common table
MASTER.Simple = {}
function MASTER:simple() end
etc.
Finally, change the first line of test_usage.lua to use a local variable:
--> test_usage.lua <--
local MASTER = require'master'
...
The tests should now pass.
I have a systematic way to solve that problem. I have refactored your module in a Git repository to show you how it works: https://github.com/catwell/dont-touch-global-namespace/commit/34b390fa34931464c1dc6f32a26dc4b27d5ebd69
The idea is that you should have the sub-parts return a function that takes the main module as an argument.
If you cheat by opening the source files in master.lua, append a header and a footer and use loadstring, you can even use them unmodified (only master.lua has to be modified, but it is more complex). Personally, I prefer to keep it explicit, which is what I have done here. I don't like magic :)
EDIT: it is very close to Andrew Stark's first solution, except I patch the MASTER table directly in the sub-modules. The advantage is that you can define several things at once, like in your simple.lua, multi.lua and reference.lua files.
We can solve the problem by changing the master file to modify the environment in which all required code is run:
--> master.lua <--
local m = {} -- The actual master table
local env = getfenv(0) -- The current environment
local sandbox = { MASTER=m } -- Environment for all requires
setmetatable(sandbox,{__index=env}) -- ...also exposes read access to real env
setfenv(0,sandbox) -- Use the sandbox as the environment
-- require all files as before
setfenv(0,env) -- Restore the original environment
return m
The sandbox is an empty table that inherits values from _G but that also has a reference to the MASTER table, simulating a global from the perspective of later code. Using this sandbox as the environment causes all later requires to evaluate their "global" code in this context.
We save the real environment for later restoration, so that we don't mess with any later code that might want to actually set a global variable.
The question concerns:
Not polluting the global space when making modules.
Making modules in such a way that they might be split into multiple files, for maintenance reasons, among others.
My solution to the above problem lies in tweaking the "return as table" idiom in Lua such that instead of returning a table, you return a function that returns a table, when state needs to be passed between sub-modules.
This works well for sub-modules that are entirely dependent upon some root-module. If they are loaded independently, then they require the user to know that they need to call the module before they can use it. This is unlike every other module that has a collection of methods, ready to go from local a = require('a').
At any rate, this works like so:
--callbacks.lua a -- sub-module
return function(self)
local callbacks = {}
callbacks.StartElement = function(parser, elementName, attributes)
local res = {}
local stack = self.stack
---awesome stuff for about 150 lines...
return callbacks
end
To use it, you can...
local make_callbacks = require'callbacks'
self.callbacks = make_callbacks(self)
Or, better yet, simply call the return value of require when assigning the callback table to the parent module, like so:
self.callbacks = require'trms.xml.callbacks'(self)
Most often, I try not to do this. If I'm passing state or self between submodules, I find that I'm often doing it wrong. My internal policy is that if I'm doing something that is highly-related to another file, I might be okay. More likely, I'm putting something in the wrong spot and there is a way to do it without passing anything between modules.
The reason that I don't like this is that which I pass by table has methods and properties unseen in the file that I am working within. I'm not free to refactor the internal implementation of one of my files, without horking the others. So, I humbly suggest that this idiom is a yellow flag, but probably not a red one. :)
While this solves the problem of state-sharing without globals, it doesn't really protect the user from the accidental omission of local. If I may speak to that implied question...
The first thing that I do is remove access to the global environment from my module. Remembering that it's only available as long as I don't
reset _ENV, reseting it is the first thing that I do. This is done by packing only what is needed into a new _ENV table.
_ENV = {print = print,
pairs = pairs, --etc
}
However, constantly re-typing all of the things that I need from lua into each file is a giant, error-prone pain. To avoid this, I make one file in my module's base directory and use it as the home for all of my modules' and sub-modules' common environments. I call it _ENV.lua.
Note: I cannot use "init.lua" or any other root-module for this purpose, because I need to be able to load it from the sub-modules, which are being loaded by
the root-module, which loads the sub-modules, which are...
My abbreviated _ENV.lua file looks something like the following:
--_ENV.lua
_ENV = {
type = type, pairs = pairs, ipairs = ipairs, next = next, print =
print, require = require, io = io, table = table, string = string,
lxp = require"lxp", lfs = require"lfs",
socket = require("socket"), lpeg = require'lpeg', --etc..
}
return _ENV
With this file, I now have a common base from which to work.
All of my other modules load this first, using the following command:
_ENV = require'root_mod._ENV' --where root_mod is the base of my module.
This facility was critical for me, for two reasons. First, it keeps me
out of global space. If I see that I am missing something from the global environment _G (took me a surprisingly long time before I saw that I didn't have
tostring!), I can go back into my _ENV.lua file and add it. As
a required file, this only gets loaded one time, so having it applied
to all of my submodules is 0 calories.
Second, I find that it gives me everything that I really needed for using
the "return module as table" protocol, with only a few exceptions where "return a function that returns a table" is needed.
TL;DR: Don't return the module, set package.loaded[...] = your_module as early as possible (can still be empty), then just require the module in submodules and it will be properly shared.
The clean way to do this is to explicitly register the module and not rely on require to implicitly register it at the end. The documentation says:
require (modname)
Loads the given module. The function starts by looking into the
package.loaded table to determine whether modname is already loaded.
If it is, then require returns the value stored at
package.loaded[modname]. [This gets you the caching behavior that
every file is run only once.] Otherwise, it tries to find a loader for
the module. [And one of the searchers is looking for Lua files to run,
which gets you the usual file loading behavior.]
[…]
Once a loader is found, require calls the loader with two arguments:
modname and an extra value dependent on how it got the loader. (If the
loader came from a file, this extra value is the file name.) If the loader
returns any non-nil value [e.g. your file returns the module table],
require assigns the returned value to package.loaded[modname]. If the
loader does not return a non-nil value and has not assigned any value to
package.loaded[modname], then require assigns true to this entry.
In any case, require returns the final value of
package.loaded[modname].
(emphasis, [comments] added by me.)
With the return mymodule idiom, the caching behavior fails if you have a loop in your dependencies – the cache is updated too late. (As a result, files may be loaded several times (you may even get endless loops!) and sharing will fail.) But explicitly saying
local _M = { } -- your module, however you define / name it
package.loaded[...] = _M -- recall: require calls loader( modname, something )
-- so `...` is `modname, something` which is shortened
-- to just `modname` because only one value is used
immediately updates the cache, so that other modules can already require your module before its main chunk returned. (Of course, at that time they can only actually use what's already been defined. But that's not usually a problem.)
The package.loaded[...] = mymodule approach works in 5.1–5.3 (incl. LuaJIT).
For your example, you would adjust the start of master.lua to
1c1,2
< MASTER = {}
---
> local MASTER = {}
> package.loaded[...] = MASTER
and for all other files
0a1
> local MASTER = require "master"
and you're done.