Language server: requests about an opened file fail with "File not found" when the startup scan skipped it

Open
#1,946 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
rust

Research direction

Start with the functionality/*.rs handlers named in the issue, especially document_symbols.rs and inlay_hints.rs, and trace how they find documents after textDocument/didOpen. Reproduce using the provided LSP steps with a file skipped by the startup scan, then check whether the request sees the opened document or waits for it to load. Done when the immediate request succeeds without File not found.

Written by the indexing model from the issue text.

Description

When a file is outside the language server's startup scan, the first requests about it fail with File not found, even after the client has sent textDocument/didOpen for it. VS Code restores open tabs on a window reload, so the reload shows error toasts:

[Error] Request textDocument/documentSymbol failed.
  Message: There was an internal error... CustomError(ResponseError { code: ErrorCode(-32603), message: "File not found: file:///…/a.fe", data: None })
[Error] Request textDocument/inlayHint failed.
  …same message…

Layout. Any file the startup scan skips will do. For example, a workspace with one member and a file outside it:

fe.toml          [workspace] members = ["lib"]
lib/fe.toml      [ingot] name = "lib"
lib/src/lib.fe   pub fn f() -> u256 { 1 }
scratch/a.fe     fn g() -> u256 { 2 }

Steps. Start fe lsp with the directory as its root, send initialized, then didOpen for scratch/a.fe, then right away textDocument/documentSymbol for it.

  • Result: File not found. The server still publishes diagnostics for the file, so it did receive the didOpen.
  • If the client waits about 15 s after didOpen before the request, the request succeeds.
  • A file that the startup scan loaded, such as a standalone file in a directory without fe.toml, does not fail this way.

Expected: a request about an opened document uses the text from didOpen, or waits until the server has loaded it.

Reproduces on 26.5.1 (149c758) and on master b182b0126, on x86-64 Linux. File not found comes from the functionality/*.rs handlers, such as document_symbols.rs:27 and inlay_hints.rs:26.

The LSP client used (Python, standard library only)

Usage: FE=path/to/fe python3 client.py <root> <file to open>

import json, subprocess, sys, time, threading, collections
root = sys.argv[1]; opened = sys.argv[2:]
p = subprocess.Popen([__import__("os").environ["FE"], "lsp"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
def send(m):
    b = json.dumps(m).encode(); p.stdin.write(b"Content-Length: %d\r\n\r\n" % len(b) + b); p.stdin.flush()
diags = collections.OrderedDict(); MSG = {}
def reader():
    while True:
        h = {}
        while True:
            line = p.stdout.readline()
            if not line: return
            line = line.strip()
            if not line: break
            k, v = line.split(b":", 1); h[k.lower()] = v.strip()
        m = json.loads(p.stdout.read(int(h[b"content-length"])))
        if m.get("method") == "textDocument/publishDiagnostics":
            diags[m["params"]["uri"]] = len(m["params"]["diagnostics"]); MSG[m["params"]["uri"]] = [d.get("message","")[:90] for d in m["params"]["diagnostics"]]
        elif m.get("id",0) >= 100 and "method" not in m:
            print("response", m["id"], "error" if "error" in m else "ok", str(m.get("error",""))[:150], flush=True)
        elif "id" in m and "method" in m:
            send({"jsonrpc": "2.0", "id": m["id"], "result": None})
threading.Thread(target=reader, daemon=True).start()
send({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":None,"rootUri":"file://"+root,"workspaceFolders":[{"uri":"file://"+root,"name":"w"}],"capabilities":{}}})
time.sleep(3); send({"jsonrpc":"2.0","method":"initialized","params":{}})
for f in opened:
    send({"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://"+f,"languageId":"fe","version":1,"text":open(f).read()}}})
for i,f in enumerate(opened):
    send({"jsonrpc":"2.0","id":100+i,"method":"textDocument/documentSymbol","params":{"textDocument":{"uri":"file://"+f}}})
time.sleep(40)
for u, n in diags.items():
    if n: print(n, MSG[u], u.replace("file://"+root+"/", ""))
print("files with diagnostics:", sum(1 for n in diags.values() if n), "of", len(diags))
p.kill()
Dominant language
Rust
Stars
1.7k
Forks
219
Avg merge
1d 3m
Merged PRs (30d)
162

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from argotorg/fe

All issues in argotorg/fe

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.