来自 awesome wm 的这段 lua 代码有什么作用?

What does this piece of lua code from awesome wm do?

看看这段代码:

local urgent = {}

local capi =
{
    client = client,
}

local client
do
    client = setmetatable({}, {
        __index = function(_, k)
            client = require("awful.client")
            return client[k]
        end,
        __newindex = error -- Just to be sure in case anything ever does this
    })
end

我无法理解它的作用。它来自 awesome-wm 项目。这些是我难以理解的事情:

  1. client = clientcapi
  2. 的声明中
  3. setmetatable里面的东西do-end
  1. client = client in the declaration of capi

这是定义 capi 的哪一部分在此文件的范围内可用,如果您查看 client.lua 文件,您会看到其中定义的 capi 具有客户端、鼠标、屏幕, 太棒了。

对于 capi table 中定义的每个项目,都有一个对应的 .c 文件。这些文件定义了 client 等对象。 urgent.lua 具有该对象的可见性,可能它是一个全局变量,这就是我们如何设置 client = client 第二个客户端引用全局变量。

以下是 2 个文件的示例:

main.lua

bar = "Hello World!"

local foo = require('foo')

print(foo.bar)

foo.lua

local foo = {
    bar = bar
}
return foo

main.lua 中的打印函数将导致 Hello World!

  1. setmetatable stuff inside do-end

这里通过扭曲 do-end 块中的 setmetatable 代码在受限范围内执行。通常这样做是为了包含块的局部变量,以便它们在代码执行后不会持续存在。

那不是这个块的目的,因为这个块没有局部变量。如我所见,阻塞只是为了表明被修改的对象是client的局部变量而不是客户端的全局变量.

另外这里的metatable是用来防止循环依赖的,在项目中一些类似代码出现的地方有注释提到,比如client.lua where local screen 已定义。

@Nifim 回答得很好。我只是想添加更多关于 为什么 此代码存在于其适当的历史背景中的上下文。在 Lua 5.2 之前,模块系统不同。在核心 Lua 库中定义了一个神奇的 module() 函数。当你制作一个模块时,你 必须 在调用 module() 之前首先制作所有全局变量的本地版本,否则它会 运行 在它自己的全局环境中。 "capi" 代表 "Core API" 或 "C (language) API" 取决于天气。如果 Awesome 是今天用我们现在拥有的所有知识编写的,就不会有 public "C language" API 并且它们将始终隐藏在私有部分以增加灵活性。现在设置 "c.my_own_property" 在 capi.client 和 awful.client 之间进行几次往返只是为了适应所有遗留约束。

现在,元表魔法是一种称为元延迟加载的 Lua 模式。因为 urgentawful.client 的子模块,所以不能直接导入 awful.client 而不会造成循环依赖。随着时间的推移,随着 Awesome API 的定义变得更好,进行了越来越多的重构,并且它们经常引入奇怪的依赖关系以保持一定程度的向后兼容性。在最好的情况下,我们会忽略所有用户配置并重新设计整个代码以避免这些循环依赖。然而,每次我们这样做时,上述 API 的所有用户都会在一天早上醒来,他们无法再登录到他们的计算机。因此,存在这种解决方法是为了防止 return 中出现此类事件,以解决一些奇怪的代码和维护负担。