Skip to content

How to apply different libraries to different files? #2157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
zhantaram opened this issue Jun 6, 2023 · 12 comments
Open

How to apply different libraries to different files? #2157

zhantaram opened this issue Jun 6, 2023 · 12 comments
Labels
question User has a question

Comments

@zhantaram
Copy link

zhantaram commented Jun 6, 2023

How are you using the lua-language-server?

Visual Studio Code Extension (sumneko.lua)

Which OS are you using?

MacOS

What is the issue affecting?

Libraries

Expected Behaviour

Same variables refer to different definitions, depending on file

Actual Behaviour

Same variables do not refer to different definitions, depending on file

Reproduction steps

Create several libraries with different definitions for some global

Additional Notes

Hi!

This is a question and not a bug report. Imagine, that I have several lua files with global variable named SomeVar:

file1.lua

print(SomeVar.X)

file2.lua

print(SomeVar.Y)

and there are two different files with annotations:
addon1/library/annotations1.lua

---@class TSomeVar1
---@field X integer
SomeVar = {}

addon2/library/annotations2.lua

---@class TSomeVar2
---@field Y integer
SomeVar = {}

Is there any way to specify that for file "file1.lua" first library should be used, and for "file2.lua" second library should be used?
I think that similar question was referred in this issue #119
But I cannot run many language servers, because number of files can grow very large

Log File

No response

@zhantaram
Copy link
Author

I think that this will do just right #2098

Are you going to implement it any time soon?

@zhantaram
Copy link
Author

zhantaram commented Jun 7, 2023

I think I can solve my problem with this code:
file1.lua

SomeVar = SomeVar --[[as TSomeVar1]]

file2.lua

SomeVar = SomeVar --[[as TSomeVar2]]

but this seems like a bad solution

@sumneko
Copy link
Collaborator

sumneko commented Aug 11, 2023

Using the "multi-workspaces" feature of VSCode.

@sumneko sumneko added the question User has a question label Aug 11, 2023
@rhys-vdw
Copy link

rhys-vdw commented Mar 26, 2025

Using the "multi-workspaces" feature of VSCode.

lua-language-server is not just a VSCode plugin, it's a language server with a CLI. We need a way to define the project in a way that is IDE agnostic so that we can (for example) run lua-language-server --check on the project.

@tomlau10
Copy link
Contributor

I believe this is unachievable in single workspace mode, since only 1 config is allowed in the same workspace.
This is how LuaLS is designed. Here is an example of how to setup multi root workspace and apply different libraries to different workspace: #3030 (comment)

But actually the support of multi-workspace is not specific to vscode:
if a client sends the params.workspaceFolders[] array to server instead of just sending params.rootUri while initializing, then luals is said to be running in multi-root workspace mode (correct me if I am wrong).

m.register 'initialize' {
function(params)
client.init(params)
if params.rootUri then
workspace.initRoot(params.rootUri)
cap.resolve('ROOT_PATH', furi.decode(params.rootUri):gsub('\\', '/') .. '/')
end
if params.workspaceFolders then
for _, folder in ipairs(params.workspaceFolders) do
workspace.create(files.getRealUri(folder.uri), folder.name)
end
elseif params.rootUri then
workspace.create(files.getRealUri(params.rootUri))
end


so that we can (for example) run lua-language-server --check on the project.

So now this is another problem/issue, that currently there is no way to do a --check on a project which uses multi-root workspace feature. By reading the check_worker.lua, I see that it actually uses a fake client to send the initialize request to server:

xpcall(lclient.start, errorhandler, lclient, function (client)
await.disable()
client:registerFakers()
client:initialize {
rootUri = rootUri,
}

  • maybe something can be done here?
  • for example if we pass a *.code-workspace file to --check=, then check worker should try to decode this json file, and construct the correct workspaceFolders[] params for initialization 🤔

@rhys-vdw
Copy link

rhys-vdw commented Mar 26, 2025

Something I'm confused about... Can I define a global such that it's only available if required via a ---@module annotation? That would solve the problem.

For example if I could do:

library:

---a.lua
---@meta

A = { foo = 1 }
---b.lua
---@meta

B = { bar = 2 }

app:

---@module "a"

A.foo -- good!
B.bar -- error, global B not declared

@tomlau10
Copy link
Contributor

Can I define a global such that it's only available if required via a ---@module annotation?

I don't think so 🤔 at least not in the current state.

First of all I think that the description for @module on the wiki is a bit misleading:

  • it is for binding the followed variable as requiring the specified module
  • i.e. simply writing a single line ---@module "library" has no effect
  • it's usage is for binding the return value of the library to a variable
---@module "library"
local library
--> this has the same effect as `local library = require "library"`

You can verify this by full searching doc.module in the LuaLS codebase, I believe the only relevant logic is here:

if doc.type == 'doc.module' then
local name = doc.module
if not name then
return true
end
local uri = rpath.findUrisByRequireName(guide.getUri(source), name)[1]
if not uri then
return true
end
local state = files.getState(uri)
local ast = state and state.ast
if not ast then
return true
end
vm.setNode(source, vm.compileNode(ast))

However, I have no idea whether this is an unfinished feature or not.


Back to the (multi) workspace concept, assuming my understanding is correct, a single workspace means a single runtime environment, that's why a single workspace can only have 1 set of config:

  • If you define a global variable in this single runtime, surely every scripts within this runtime will notice this global variable
  • therefore if you have two runtimes (like a classic server/ and client/ structure), or just want a separate runtime environment for luals to do inferences, I think you will need the multi workspace feature 😕

@rhys-vdw
Copy link

rhys-vdw commented Mar 26, 2025

Okay, multi-workspace sounds theoretically possible. However, this is a giant 20 year old codebase with, for example, a folder with 30 or so files that are each in a completely isolated (but identical) environment. That is, they all have access to the same global, but it's their own unique copy.

I don't think I can reasonably create that many lua configs. It would confuse everyone who is used to working on the project, and because an unreasonable maintenance burden.

Without a doubt, the best solution for this project would be to be able to @module a file that defines the global scope, or to explicitly set the type of the _G table like: ---@global SomeClass or ---@cast _G SomeClass.

I am not sure how I'll proceed but we can still get value out of it even if the types have to lie in many cases.

@tomlau10
Copy link
Contributor

this is a giant 20 year old codebase with, for example, a folder with 30 or so files that are each in a completely isolated (but identical) environment

I don't quite get the situation.
You mean in a folder of your legacy project, there are many files in which all of them share the same libraries, but each will run on their own? (such that they have their unique copy of the "identical global environment")

I have absolutely no idea how to tackle this ... 🙈
Because how LuaLS works is just preloading all files within the single workspace, and assumed that all of them will be used in this single runtime environment.
Therefore if any of the file defined a global variable, then this single workspace runtime env will have this global value.


Assumed vscode will be used, I don't know if a *.code-workspace can define a single file as a workspace 🤔 ?

  • If it's possible, then maybe you can just create one *.code-workspace to define each of your 30 or so files as different workspace
  • and then add all the shared luals settings into the settings section of this *.code-workspace 😕
{
  "settings": {
    // all settings under this section will be shared among all defined workspace
    "Lua.xxx": "yyy",
  }
}

https://code.visualstudio.com/docs/editing/workspaces/multi-root-workspaces#_settings

  • in short, it will be like putting the .luarc.json content into the name.code-workspace file
    to achieve the "identical global environment" for all workspaces but each have their unique copy 🤔 🤔

@rhys-vdw
Copy link

I don't quite get the situation.

Well, essentially there are many different types of entrypoint into the game, and they have access to different APIs via different injected globals. Sometimes these files are siblings in the same folder, so I can't just slap a .luarc.json in there.

You mean in a folder of your legacy project, there are many files in which all of them share the same libraries, but each will run on their own? (such that they have their unique copy of the "identical global environment")

Yeah, kinda. tbh the system is kind of complex and I don't fully understand it. But essentially the issue is the above, I just need to be able to map certain global environments to different files. I might be able to get there with many different .luarcs. I've yet to try nesting them.

Assumed vscode will be used, I don't know if a *.code-workspace can define a single file as a workspace 🤔 ?

That's not necessarily the case. Plenty of NeoVim users etc. I could explore that options though, it's an interesting thought. But even then it will just immediately become unmaintained. What is needed (ideally) is the file itself can just claim to be in a scope so it can benefit from intellisense etc.

I am not an experienced Lua user, but is it the case that in a typical Lua project all globals appear in all other files, even if they are not required into each other etc?

@tomlau10
Copy link
Contributor

I am not an experienced Lua user, but is it the case that in a typical Lua project all globals appear in all other files, even if they are not required into each other etc?

With eight years of experience in server-side game development, I've primarily worked on projects using Lua. In our development team, we have a strict convention against using globals. By adhering to this best practice, the issue of globals appearing across different files and causing potential conflicts is non-existent in our projects. 😂

However, it's important to note that my experience doesn't represent the entire Lua community.

@rhys-vdw
Copy link

Don't take any of my description of the project to be a defence or a claim of best-practise. It is simply problem I'm solving.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question User has a question
Projects
None yet
Development

No branches or pull requests

4 participants