Cross-file semantic analysis in zls

here’s a trivially simple Common.zig file:

pub const MyInt = u32;

this file is then imported in another file as follows:

const Common = @import("./Common.zig");
const MyInt = Common.MyInt;

when sitting in vscode, the symbols MyInt and Common are categorized as “types” through semantic tokens returned by a running zls server; without this information, these identifiers are “variables” and would be colorized accordingly…

i also have a standalone tool which uses "zls" as a module, just as the test programs do in the zls repository… by sending zls the "textDocument/didOpen" message followed by the "textDocument/semanticTokens/full" message i can receive a similar set of semantic tokens for a given source file…

but here’s the part i’m missing… to correctly “know” that MyInt is a type requires also ingesting "./Common.zig"… and the zls attached to vscode seems to do just that…

but with my standalone case (which starts with a “clean” zls), asking it to analyze my top-level source file yields results suggesting that my standalone zls has not also seen "./Common.zig"

perhaps there is some caching in zls i need to understand??? and given multiple folders of multiple .zig files, do i essentially have to pass all of them into my standalone zls before asking for semantic tokens???

I don’t see a reason why this shouldn’t be possible to get working. If I had to guess what is going wrong, I would check the uri in the params of textDocument/didOpen. ZLS will use it to load imports like Common.zig from the filesystem unless you also use a textDocument/didOpen notification for that file. It is hard to tell without having access to the code.

I believe that ZLS will log a error or warning if it failed to load a file from disk. That may help with figuring out what happened.

i’m attaching some code…
Renderer.zig (4.6 KB)

the exec function at the bottom of the file prepares the semantic token request and the prints the tokens return… assume the file is:

const Common = @import("./Common.zig");
const MyInt = Common.MyInt;

i get a stream of semantic tokens that are “correct” in that token line/col information is as expected…

the token-type, however, is "variable" rather than "type" – as if zls hasn’t processed Common.zig… but when i look at this file in vscode, the server trace has “type” ascribed to Common and MyInt as expected…

The code doesn’t show what path is being passed to the exec function. Does the file actually exist on the filesystem with the given path? Is the Common.zig also located there? Is ZLS logging an error? You should be able to read stderr from the ZLS child process. Or just look for the zls.json if available.

here’s Common.zig:

pub const MyInt = u32;

here’s test.zig:

const Common = @import("./Common.zig");
const MyInt = Common.MyInt;

put these files in some directory, and then call exec("./test.zig")… if you uncomment lines 145-148, you’ll see output which classifies Common and MyInt as a “variable”…

if you then open ./test.zig inside of vscode, the logs of traffic with the backend zls server shows these symbols as a “type” (which is what i’d expect)…

What does Fs.normalize("./test.zig") do? Does it return an absolute path? The uri in the "textDocument/didOpen" notification is used to lookup imported files.

You should look in the ZLS logs. You should be able to post it here so I could have a look at it. They should make it clear if ZLS was able to load the Common.zig file.

Renderer.zig is not self contained. I can’t run this code.

i’m so sorry !!!

let me try to get you some log output…

i’m currently running on zls 0.13.0, which doesn’t appear to expose logging in the same way as zls 0.14.0-dev… the zls env command doesn’t work, for instance…

i am building zls from sources (debug mode); is there something in zls 0.13.0 i could retrieve???

and most important, i am NOT running zls as a separate process (as is the case with vscode)… rather, i am calling server.create() directly from my client app…

i quite familiar with enabling debug logs and message trace from within vscode… i just don’t see how to do the equivalent using methods on my server instance…

again, apologizes for the confusion…

ok, here we go…

this is debug log from within vscode, where i’m launching vscode and then immediately opening my test.zig file in the editor… as you can see from the log, zls ALSO opens Common.zig – though i’ve never explicitly selected that file…

info : ( main ): Starting ZLS 0.13.0 @ 'c:\Users\biosb\zig\zigem-dev/zig-out/bin/zls-em.exe'
info : (server): Client is 'Visual Studio Code-1.93.1'
debug: (server): Offset Encoding: utf-16
info : (server): No config file zls.json found. This is not an error.
warning: (config): ignoring entry in PATH 'C\zig.exe' because it is not an absolute file path
info : (server): Set config option 'builtin_path' to 'C:\Users\biosb\AppData\Local\Temp\zls\builtin.zig'
info : (server): Set config option 'zig_lib_path' to 'C:\tools\zig-windows-x86_64-0.13.0\lib'
info : (server): Set config option 'zig_exe_path' to 'C:\tools\zig-dev\zig.exe'
info : (server): Set config option 'build_runner_path' to 'C:\Users\biosb\AppData\Local\Temp\zls\build_runner\21872970afd69e48a0847077e5196711\build_runner.zig'
info : (server): Set config option 'global_cache_path' to 'C:\Users\biosb\AppData\Local\Temp\zls'
debug: (server): Took 168ms to process request-0-initialize on Thread 10008
debug: (server): Dynamically registering method 'workspace/didChangeConfiguration'
debug: (server): Took 0ms to process notification-initialized on Thread 10008
debug: (server): Took 0ms to process response-register-workspace/didChangeConfiguration on Thread 10008
info : (server): Set config option 'semantic_tokens' to 'partial'
info : (server): Set config option 'enable_inlay_hints' to 'false'
info : (server): Set config option 'inlay_hints_exclude_single_argument' to 'false'
info : (server): Set config option 'inlay_hints_hide_redundant_param_names_last_token' to 'true'
info : (server): Set config option 'skip_std_references' to 'true'
info : (server): Set config option 'zig_lib_path' to 'c:\tools\zig-dev\lib'
info : (server): Set config option 'zig_exe_path' to 'C:\tools\zig-dev\zig.EXE'
debug: (server): Took 194ms to process response-i_haz_configuration on Thread 10008
debug: (server): Took 0ms to process notification-$/setTrace on Thread 10008
debug: (server): Took 0ms to process notification-workspace/didChangeConfiguration on Thread 10008
debug: (server): Took 0ms to process response-semantic_tokens_refresh on Thread 10008
debug: (server): Took 202ms to process response-i_haz_configuration on Thread 10008
debug: (server): Took 0ms to process response-semantic_tokens_refresh on Thread 10008
debug: (store ): Opened document `file:///c%3A/Users/biosb/zig/zigem-dev/workspace/em.core/.junk/test.zig`
debug: (server): Took 2ms to process notification-textDocument/didOpen on Thread 10008
debug: (server): Took 0ms to process request-1-textDocument/documentSymbol on Thread 29460
debug: (store ): Opened document `file:///c%3A/Users/biosb/zig/zigem-dev/workspace/em.core/.junk/common.zig`

before i turn to my own app, let me understand one thing: the textDocument/didOpen message was sent to zls when i explicitly opened test.zig in the editor… but zls ALSO opened Common.zig at this time, even though no textDocument/didOpen message was sent for this file…

zls presumably parsed the @import("./Common.zig") statement inside test.zig and parsed Common.zig as well…

but when i run my command line app, i see no evidence of Common.zig being opened when i explicitly perform a textDocument/didOpen request on test.zig… why is this “working” under vscode, but not within my command line app???

The textDocument/didOpen notification is meant to exchange/synchronize a document from the client to the server without the server having to read the file from disk. The server is still allowed to read any additional files from the filesystem as it needs. When ZLS tries to analyse the @import("./Common.zig") (e.g. when sending textDocument/semanticTokens/full) then it starts to load the Common.zig file. If it hasn’t previously been exchanged with textDocument/didOpen then it will read it from the filesystem.

You should look at the ZLS logs from your command line app and not vscode. Ideally with debug messages enabled:

pub const std_options: std.Options = .{
    .log_level = .debug,
};
1 Like

my bad !!! my url was missing a leading ‘/’ !!!

every works just fine now…

thank you again for patience :pray: