Overriding the package.loaded table in a function in Lua - lua

I'm trying to override the package.loaded table in the scope of a function using setfenv, but it's not quite working.
local env = {
require = require,
print = print,
package = {
cpath = package.cpath,
loadlib = package.loadlib,
path = package.path,
preload = package.preload,
seeall = package.seeall,
loaded = {
foobar = {
fn = function() print("hello world") end,
},
},
},
}
setfenv(assert(loadstring("require('foobar')")), env)()
Executing the above code, will result in a error because the module foobar isn't found.
It seems that it's for some reason not picking up the package table override that I've specified in the environment.
How do I make this work?

Related

Setup lunarvim to use prettier-eslint

I'm migrating to Lunarvim from Atom and I need to configure Lunarvim to use prettier-eslint for Javascript files.
Read lunarvim docs but not sure how to do it.
Solved with this custom LUA Config
-- optional: set your prefered indent with size
vim.opt.shiftwidth = 4
vim.opt.tabstop = 4
-- load required null-ls references
local h = require("null-ls.helpers")
local cmd_resolver = require("null-ls.helpers.command_resolver")
local methods = require("null-ls.methods")
local u = require("null-ls.utils")
local FORMATTING = methods.internal.FORMATTING
-- Define the new javascript formatter
local pe = h.make_builtin({
name = "prettier_eslint",
meta = {
url = "https://github.com/prettier/prettier-eslint-cli",
description = "Eslint + Prettier",
},
method = FORMATTING,
filetypes = {
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"jsx"
},
factory = h.formatter_factory,
generator_opts = {
command = "prettier-eslint",
args = { "--stdin", "--parser", "babel", "--resolve-plugins-relative-to", "~/.nvm/versions/node/v16.16.0/lib" },
to_stdin = true,
},
})
-- optional: Define a second formatter for JSON
local pejson = h.make_builtin({
name = "prettier_eslint_json",
meta = {
url = "https://github.com/prettier/prettier-eslint-cli",
description = "Eslint + Prettier",
},
method = FORMATTING,
filetypes = {
"json",
"cjson",
},
factory = h.formatter_factory,
generator_opts = {
command = "prettier-eslint",
args = { "--stdin", "--parser", "json" },
to_stdin = true,
},
})
-- Enable the the defined formatters
-- if you are using vanilla NeoVim + null-ls please
-- read how to install/enable on
-- https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/CONFIG.md
local nls = require("null-ls")
nls.setup {
on_attach = require("lvim.lsp").common_on_attach,
sources = {
pe,
pejson
}
}
-- optional: LunarVim related step. Here we enable eslint as linter for Javascript.
local linters = require "lvim.lsp.null-ls.linters"
linters.setup {
{
command = "eslint",
filetypes = { "javascript" }
}
}
Full tutorial here

Minimal NeoVim lua config for using nvim-cmp with lsp

I'm trying to write my own NeoVim Lua based config, stripped down to the minimum I need and with the goal to understand at least most of the config. LSP setup is working fine and is the only source I configured for nvim-cmp:
local cmp = require("cmp")
cmp.setup {
sources = {
{ name = 'nvim_lsp' }
}
}
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)
After some startup delay the completion is working in the sense that I see popups with proposed completions, based on information from LSP.
But I cannot select any of the proposed completions. I can just continue to type which reduces the proposed completions, but I cannot use tab, arrow keys, ... to select an entry from the popup. I saw in the docs that one can define keyboard mappings but cannot make sense out of them. They are all rather sophisticated, require a snippet package to be installed, ...
I would prefer to select the next completion via tab and to navigate them via arrow key. "Enter" should select the current one.
Could somebody show me a minimal configuration for this setup or point me to more "basic" docs?
Nvim-cmp requires you to set the mapping for tab and other keys explicitly, here is my working example:
local cmp = require'cmp'
local lspkind = require'lspkind'
cmp.setup({
snippet = {
expand = function(args)
-- For `ultisnips` user.
vim.fn["UltiSnips#Anon"](args.body)
end,
},
mapping = cmp.mapping.preset.insert({
['<Tab>'] = function(fallback)
if cmp.visible() then
cmp.select_next_item()
else
fallback()
end
end,
['<S-Tab>'] = function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
fallback()
end
end,
['<CR>'] = cmp.mapping.confirm({ select = true }),
['<C-e>'] = cmp.mapping.abort(),
['<Esc>'] = cmp.mapping.close(),
['<C-d>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
}),
sources = {
{ name = 'nvim_lsp' }, -- For nvim-lsp
{ name = 'ultisnips' }, -- For ultisnips user.
{ name = 'nvim_lua' }, -- for nvim lua function
{ name = 'path' }, -- for path completion
{ name = 'buffer', keyword_length = 4 }, -- for buffer word completion
{ name = 'omni' },
{ name = 'emoji', insert = true, } -- emoji completion
},
completion = {
keyword_length = 1,
completeopt = "menu,noselect"
},
view = {
entries = 'custom',
},
formatting = {
format = lspkind.cmp_format({
mode = "symbol_text",
menu = ({
nvim_lsp = "[LSP]",
ultisnips = "[US]",
nvim_lua = "[Lua]",
path = "[Path]",
buffer = "[Buffer]",
emoji = "[Emoji]",
omni = "[Omni]",
}),
}),
},
})
This is working great for me. The mapping part corresponds to the config for key mapping in the table. You can tweak your conf based on my config to make it work for you.

Lua loadstring Function fail

Given this code:
local fruit = {}
fruit.name = "Bramley"
loadstring("fruit.pips = '2'")
fruit.skinc = 'Red'
print(fruit)
Why aren't the pips added to the table:
table
'fruit'{
'name'='Bramley',
'skinc'='Red'
}
loadstring() (or load() in Lua 5.2 or higher) returns a function, you have to run that function to execute the code. Like this:
fruit = {}
fruit.name = "Bramley"
loadstring("fruit.pips = '2'")()
Note that fruit has to be global, or an error would be generated because the environment of the returned function of loadstring is the global environment.

How to split a Torch class into several files in a Lua rock

In my recently aided in the development of a Dataframe package for Torch. As the code base has quickly doubled there is a need to split the class into several sections for better organization and follow-up (issue #8).
A simple test-class would be a test.lua file in the root folder of the test-package:
test = torch.class('test')
function test:__init()
self.data = {}
end
function test:a()
print("a")
end
function test:b()
print("b")
end
Now the rockspec for this would simply be:
package = "torch-test"
version = "0.1-1"
source = {
url = "..."
}
description = {
summary = "A test class",
detailed = [[
Just an example
]],
license = "MIT/X11",
maintainer = "Jon Doe"
}
dependencies = {
"lua ~> 5.1",
"torch >= 7.0",
}
build = {
type = 'builtin',
modules = {
["test"] = 'test.lua',
}
}
In order to get multiple files to work for a single class it is necessary to return the class object initially created and pass it to the subsections. The above example can be put into the file structure:
\init.lua
\main.lua
\test-0.1-1.rockspec
\Extensions\a.lua
\Extensions\b.lua
The luarocks install/make copies the files according to 'require' syntax where each . signifies a directory and the .lua is left out, i.e. we need to change the rockspec to:
package = "torch-test"
version = "0.1-1"
source = {
url = "..."
}
description = {
summary = "A test class",
detailed = [[
Just an example
]],
license = "MIT/X11",
maintainer = "Jon Doe"
}
dependencies = {
"lua ~> 5.1",
"torch >= 7.0",
}
build = {
type = 'builtin',
modules = {
["test.init"] = 'init.lua',
["test.main"] = 'main.lua',
["test.Extensions.a"] = 'a.lua',
["test.Extensions.b"] = 'b.lua'
}
}
The above will thus create a test-folder where the packages reside together with the files and subdirectories. The class initialization now resides in the init.lua that returns the class object:
test = torch.class('test')
function test:__init()
self.data = {}
end
return test
The subclass-files now need to pickup the class object that is passed using loadfile() (see init.lua file below). The a.lua should now look like this:
local params = {...}
local test = params[1]
function test:a()
print("a")
end
and similar addition for the b.lua:
local params = {...}
local test = params[1]
function test:b()
print("b")
end
In order to glue everything together we have the init.lua file. The following is probably a little over-complicated but it takes care of:
Finding all extensions available and loading them (Note: requires lua filesystem that you should add to the rockspec and you still need to add each file into the rockspec or it won't be in the Extensions folder)
Identifies the paths folder
Loads the main.lua
Works in a pure testing environment without the package installed
The code for init.lua:
require 'lfs'
local file_exists = function(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end
-- If we're in development mode the default path should be the current
local test_path = "./?.lua"
local search_4_file = "Extensions/load_batch"
if (not file_exists(string.gsub(test_path, "?", search_4_file))) then
-- split all paths according to ;
for path in string.gmatch(package.path, "[^;]+;") do
-- remove trailing ;
path = string.sub(path, 1, string.len(path) - 1)
if (file_exists(string.gsub(path, "?", "test/" .. search_4_file))) then
test_path = string.gsub(path, "?", "test/?")
break;
end
end
if (test_path == nil) then
error("Can't find package files in search path: " .. tostring(package.path))
end
end
local main_file = string.gsub(test_path,"?", "main")
local test = assert(loadfile(main_file))()
-- Load all extensions, i.e. .lua files in Extensions directory
ext_path = string.gsub(test_path, "[^/]+$", "") .. "Extensions/"
for extension_file,_ in lfs.dir (ext_path) do
if (string.match(extension_file, "[.]lua$")) then
local file = ext_path .. extension_file
assert(loadfile(file))(test)
end
end
return test
I hope this helps if you run into the same problem and find the documentation a little too sparse. If you happen to know a better solution, please share.

Using __call to create a DSL with lua, how to implement subsections?

I am new to lua and I am trying to create a configuration DSL which allows to have sections that already have defaults.
So, the java table is predefined with lot of values
java = {
source = 1.6,
target = 1.6,
directories = {
sources = "src/main/java",
output = "build/clases",
},
}
I have a Config prototype that implements __call so that when called as a function with a table constructor, it only overwrites the defaults. Something (like) this:
Config.__call = function(t, props)
for k,v in pairs(props) do
t[k] = v
end
end
The idea is that you can call the dsl only to specify what you want to override:
java {
source = 1.5,
directories {
sources = "customsrcdir",
}
}
There is a Config.new method that allows to apply the prototype recursively to the tables so that all have a metatable with the __call method set.
My problem is with the "directories" subsection. It is evaluated in the global context, so the only way this works is:
java {
source = 1.5,
java.directories {
sources = "customsrcdir",
}
}
Which is pointless, as this is the same as doing:
java {
source = 1.5
}
java.directories {
sources = "customsrcdir",
}
I tried different approaches to have the desired DSL to work. One was setting a custom global environment with _ENV, but then I realized the table is evaluated before __call.
I wonder if someone with more lua experience has implemented a DSL like this using more advanced table/metatable/_ENV magic.
It's possible to do it your way with calls, but the solution's so convoluted that it's not worth the omission of the =. If you still want the table merge/replacement functionality, then that's not too difficult.
local function merge(t1, t2)
for k, v in pairs(t2) do
-- Merge tables with tables, unless the replacing table is an array,
-- in which case, the array table overwrites the destination.
if type(t1[k]) == 'table' and type(v) == 'table' and #v == 0 then
merge(t1[k], v)
else
t1[k] = v
end
end
end
local data = {
java = {
source = 1.6,
target = 1.6,
directories = {
sources = "src/main/java",
output = "build/classes",
},
}
}
local dsl = {}
load( [[
java = {
source = 1.5,
directories = {
sources = "customsrcdir",
},
}
]], 'dsl-config', 't', dsl)()
merge(data, dsl)
Dumping data will result in:
java = {
directories = {
output = "build/classes",
sources = "customsrcdir"
}
source = 1.5,
target = 1.6
}
Check out how premake does it... Might be a more elegant solution than what you have going right now. http://en.wikipedia.org/wiki/Premake#Sample_Script

Resources