omnisharp-roslyn: Imports not detected when client sends only `textDocument/didOpen` on newly created file (Unity project, neovim client)

Hi!

I’m trying to troubleshoot some issues the client I work on has with omnisharp-roslyn, and I was hoping I could ping you all for help.

The issue is, when opening a unity project, say one containing the following file:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

Everything is fine, our client detects the imports no problem.

However, the minute we open a new file (for which we send the requisite textDocument/didOpen notification) with the exact same content, in the exact same subdirectory, the server returns import error messages:

The type or namespace name 'UnityEngine' could not be found (are you missing a using directive or an assembly reference?) irective or an assembly reference?)

Other servers seem to be fine recognizing new files this way. Do we need to send an additional notification when creating a new file such as workspace/didChangeWatchedFiles?

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 6
  • Comments: 30 (5 by maintainers)

Most upvoted comments

I believe that I just got it to work.

  • You should be using a build for Neovim that includes the changes in the PR above (link), I recommend just using bob to install Neovim nightly
  • Your LSP Capabilities table should include workspace.didChangeWatchedFiles.dynamicRegistration = true (Neovim news.txt), example:
local cmp_nvim_lsp = require("cmp_nvim_lsp")
local capabilities = cmp_nvim_lsp.default_capabilities()
capabilities = vim.tbl_deep_extend("force", capabilities, {
    workspace = {
        didChangeWatchedFiles = {
            dynamicRegistration = true,
        },
    },
})
// pass `capabilities` to your LSP server `setup()`

I only tested this by creating the file through Unity, so I think it automatically adds the file to the Assembly-CSharp.csproj. If creating files directly from Neovim, there’s the need of using a package in the Unity side, like this one: https://github.com/niscolas/com.unity.ide.neovim-fork I didn’t test it recently, but it should work, it also adds Neovim as an External Editor option in Unity’s settings

A workaround I have found is to add the following line to Assembly-CSharp.csproj: <Compile Include="**/*.cs" />

As mentioned above, each Unity code file needs to have <Compile..../> element in the .csproj file. On neovim (using OmniSharp Roslyn LSP server), it seems like the .csproj file (specifically Assembly-CSharp.csproj) does not get automatically “refreshed” after it changes. The changes to .csproj file only seem to be applied after restarting neovim (and omnisharp). This doesn’t seem to be the case with vscode, however. When making changes to .csproj file (adding the compile element), vscode directly picks up the change and omnisharp server seems to be working as intended.

I believe “Regenerate Project Files” button on Unity just ensures there’s the <Compile.../> tag in the .csproj file for newly created/renamed files. There is a Unity plugin for vscode (and a modified version to work with neovim) that automatically regenerates the project files when there’s a new file. This is done in Unity though.

I doubt my workaround is going to be a good solution in the long run, mostly because Unity generates this file, which will override the line I’ve described above.

Is this a new file that has been saved and exists on disk?

It’s a new file that has not been added to the project. If I’m understanding you correctly then you must regenerate the solution files after the addition of a new file for omnisharp to pick up the imports correctly?

It seems to work fine in vscode creating a new file without regenerating the solution file in unity, no errors are thrown (left). Opening the same file in neovim (right) yields a bunch of errors.

image

Edit: After reviewing what happens when the FileChangeWatcher fires, there is a heuristic in O# that will match a new code file to an existing project based on whether the code file exists alongside or beneath the project file on the filesystem. This matching would not persist between sessions unless the project file was updated with the appropriate <Compile> element.

Is it possible that vscode is doing something on each startup that adds this file?

It seems like, on further glance, that vscode is not providing completion candidates (or hover) for UnityEngine on the newly added file without regenerating the solution file.

@nyngwang How would one use this new feature to solve the new file issue?

@niscolas I finally found something related: https://github.com/neovim/neovim/pull/21293, which is a PR that could be an important factor in finding/creating the solution to the problem.

Following the idea from @nickspoons above (with my little knowledge of LSPs) I tried to find if there’s a way to make a similar request to this /filesChanged in to the Omnisharp LSP client. Unfortunately, it seems that this endpoint doesn’t exist for the LSP side. These are the endpoints for Omnisharp, it’s listed on the “v1”, but I think these endpoints are only really valid for non LSP stuff, these are the handlers for the LSP part The closest I could find was this: OmnisharpOnDidChangeWatchedFilesHandler But doesn’t seem that there’s a way to request something from it, and I don’t really know how to interact with it. Sorry for the dumbness but I don’t really know much about LSP, just leaving my little research.

I’ve been looking into this in a similar issue for OmniSharp-vim. From what I’ve seen, I believe omnisharp-vscode has a file watcher watching the .csproj file, and when it is modified (by Unity, which adds a new <Compile ...> element for the new file) then vscode sends a /filesChanged request for the csproj to OmniSharp-roslyn, which then reloads the project.

I’ve tried to mimic this /filesChanged request from OmniSharp-vim and works - the new file is now part of the project, without requiring a full OmniSharp-roslyn restart.

I’ll test that approach later. But what I’ve found is it seems like there’re 2 different problems. There’re some repos on GitHub mimicking the behavior of Unity plugins for VSCode and JetBrains Rider, like this: https://github.com/neovim/neovim/issues/17808#issue-1175601347 I even tried to improve on it myself, but even using Unity’s API to Regenerate Project Files automatically on file changed, the behavior described in this issue still occurs.