How to setup Omnisharp on Linux for Unity C# with Mono via `nvim-lspconfig`?

I’ve spent days trying to setup neovim’s LSP for Unity development in C# with Mono and I’m at a loss…

Other LSP servers related to bash, lua, etc all work as expected.

While I don’t think it should matter, it may be worth noting my current setup. I have Unity installed on my system’s Windows installation and I’m running Linux through a virtual machine. Unity related projects are then “shared” to the VM. So the Linux environment is “Unity free”, I’m just using it for the vim/neovim coding side of things. The VM is where I’ve done all of my non-Unity dev stuff so that’s why I’m trying to carry that forward into Unity related work if possible.

Currently on Ubuntu 20.04.4 LTS x86_64

$ nvim --version

NVIM v0.8.0-dev+593-g5e5374035
Build type: Release
LuaJIT 2.1.0-beta3
Compiled by root@linux-VirtualBox

Features: +acl +iconv +tui
See ":help feature-compile"

   system vimrc file: "$VIM/sysinit.vim"
  fall-back for $VIM: "/usr/local/share/nvim"

Run :checkhealth for more info

Here is what I have tried so far

lsp/configs.lua

local status_ok, lsp_installer = pcall(require, "nvim-lsp-installer")
if not status_ok then
	return
end

local lspconfig = require("lspconfig")

-- https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
local servers = {
  ...
  "omnisharp",
  ...
}

lsp_installer.setup({
	ensure_installed = servers,
})

for _, server in pairs(servers) do
	local opts = {
		on_attach = require("user.lsp.handlers").on_attach,
		capabilities = require("user.lsp.handlers").capabilities,
	}
	local has_custom_opts, server_custom_opts = pcall(require, "user.lsp.settings." .. server)
	if has_custom_opts then
		opts = vim.tbl_deep_extend("force", opts, server_custom_opts)
	end
	lspconfig[server].setup(opts)
end

lsp/handlers.lua

local M = {}

... -- Various keymaps and other customizations, trying to isolate just the relevant portions

M.on_attach = function(client, bufnr)
	-- vim.notify(client.name .. " starting...")
	-- TODO: refactor this into a method that checks if string in list
	if client.name == "tsserver" then
		client.resolved_capabilities.document_formatting = false
	end
	lsp_keymaps(bufnr)
	lsp_highlight_document(client)
end

local capabilities = vim.lsp.protocol.make_client_capabilities()

local status_ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
if not status_ok then
	return
end

M.capabilities = cmp_nvim_lsp.update_capabilities(capabilities)

return M

lsp/settings/omnisharp.lua

-- Mono
local omnisharp_bin = "mono /home/linux/.local/share/nvim/lsp_servers/omnisharp/omnisharp-mono/OmniSharp.exe"

-- Roslyn
  -- local omnisharp_bin = "/home/linux/.local/omnisharp_roslyn/run"
  -- https://neovim.discourse.group/t/setting-up-omnisharp-via-nvim-lspconfig-and-nvim-lspinstall/531/2 

local pid = vim.fn.getpid()
local util = require 'lspconfig/util'

return {
    -- https://github.com/williamboman/nvim-lsp-installer/issues/479#issuecomment-1128840405
    use_mono = true,

    cmd = { omnisharp_bin, "--languageserver" , "--hostPID", tostring(pid) };
  ...
}

(neovim) :LspInfo

1 client(s) attached to this buffer: 
 
 Client: omnisharp (id: 5, pid: 14634, bufnr: [46])
 	filetypes:       cs, vb
 	autostart:       true
 	root directory:  /home/linux/<redacated>
 	cmd:             mono /home/linux/.local/share/nvim/lsp_servers/omnisharp/omnisharp-mono/OmniSharp.exe --languageserver --hostPID 14505

Mono-Project

mono-project was installed with the following script, confirmed with --version and in being able to compile and execute the example code:

sudo apt-get install git autoconf libtool automake build-essential gettext cmake python3 curl -y

# https://www.mono-project.com/download/stable/
sudo apt install gnupg ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update && \
sudo apt install mono-complete -y

$ mono --version

Mono JIT compiler version 6.12.0.182 (tarball Tue Jun 14 22:35:00 UTC 2022)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
	TLS:           __thread
	SIGSEGV:       altstack
	Notifications: epoll
	Architecture:  amd64
	Disabled:      none
	Misc:          softdebug 
	Interpreter:   yes
	LLVM:          yes(610)
	Suspend:       hybrid
	GC:            sgen (concurrent by default)

The various lsp related plugins I am currently using are

-- LSP and related {{{
	use('folke/lsp-colors.nvim')
	use('folke/trouble.nvim')
	use('jose-elias-alvarez/null-ls.nvim')
	use('neovim/nvim-lspconfig')
	use('williamboman/nvim-lsp-installer')
}}}

-- completion {{{
	use('hrsh7th/nvim-cmp')
	use('hrsh7th/cmp-buffer')
	use('hrsh7th/cmp-nvim-lsp')
	use('hrsh7th/cmp-nvim-lua')
	use('hrsh7th/cmp-path')
	use('saadparwaiz1/cmp_luasnip')

--}}}

UPDATE 20220726 20:14

Something I noticed when running :LspLog

OmniSharp.CompositionHostBuilder: It looks like you have Mono installed which contains a MSBuild lower than 17.0.0 which is the minimum supported by the configured .NET Core Sdk.\n Try updating Mono to the latest stable or preview version to enable better .NET Core Sdk support.

Will start digging into the Mono install and with how the associated mono server is being called by Neovim’s LSP stuff…


UPDATE 20220726 20:43

$ dotnet msbuild -ver
Microsoft (R) Build Engine version 17.2.0+41abc5629 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

Seems the Neovim LSP server is using something different from what is output here… Hmm…