Merge pull request #1515 from CaueAnjos/feat/csharp-update
Some checks are pending
Set up binary cache / cachix (default) (push) Waiting to run
Set up binary cache / cachix (maximal) (push) Waiting to run
Set up binary cache / cachix (nix) (push) Waiting to run
Treewide Checks / Validate flake (push) Waiting to run
Treewide Checks / Check formatting (push) Waiting to run
Treewide Checks / Check source tree for typos (push) Waiting to run
Treewide Checks / Validate documentation builds (push) Waiting to run
Treewide Checks / Validate documentation builds-1 (push) Waiting to run
Treewide Checks / Validate documentation builds-2 (push) Waiting to run
Treewide Checks / Validate documentation builds-3 (push) Waiting to run
Treewide Checks / Validate hyperlinks in documentation sources (push) Waiting to run
Treewide Checks / Validate Editorconfig conformance (push) Waiting to run
Build and deploy documentation / Check latest commit (push) Waiting to run
Build and deploy documentation / publish (push) Blocked by required conditions

languages/csharp: update
This commit is contained in:
Ching Pei Yang 2026-04-25 22:56:01 +02:00 committed by GitHub
commit 5b4f9c6320
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 755 additions and 223 deletions

View file

@ -108,6 +108,12 @@
`languages.go.treesitter.gotmplPackage` to `languages.go.treesitter.gotmplPackage` to
{option}`vim.languages.go.treesitter.gotmpl.package` {option}`vim.languages.go.treesitter.gotmpl.package`
[CaueAnjos](https://github.com/caueanjos)
- Renamed `roslyn_ls` to `roslyn-ls`
- Turned `omnisharp-extended-lsp-nvim` into an extension disabled by default
- Turned `csharpls-extended-lsp-nvim` into an extension disabled by default
## Changelog {#sec-release-0-9-changelog} ## Changelog {#sec-release-0-9-changelog}
[SecBear](https://github.com/SecBear): [SecBear](https://github.com/SecBear):
@ -487,7 +493,8 @@ https://github.com/gorbit99/codewindow.nvim
[CaueAnjos](https://github.com/caueanjos) [CaueAnjos](https://github.com/caueanjos)
- Add razor support for `roslyn_ls` and `csharp_ls` - Added razor support for `roslyn_ls` and `csharp_ls`
- Added `csharpier` formatter to csharp language
[mputz86](https://github.com/mputz86) [mputz86](https://github.com/mputz86)

View file

@ -5,205 +5,36 @@
options, options,
... ...
}: let }: let
inherit (builtins) attrNames concatMap; inherit (builtins) elem filter attrNames;
inherit (lib.options) mkEnableOption mkOption literalExpression; inherit (lib) genAttrs getExe;
inherit (lib.types) enum;
inherit (lib.modules) mkIf mkMerge;
inherit (lib.meta) getExe;
inherit (lib.generators) mkLuaInline; inherit (lib.generators) mkLuaInline;
inherit (lib.strings) optionalString; inherit (lib.options) mkEnableOption mkOption literalExpression;
inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption deprecatedSingleOrListOf; inherit (config.vim.lib) mkMappingOption;
inherit (lib.types) enum listOf;
inherit (lib.modules) mkIf mkMerge;
inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption enumWithRename luaInline;
inherit (lib.nvim.lua) toLuaObject; inherit (lib.nvim.lua) toLuaObject;
inherit (lib.nvim.dag) entryAnywhere;
inherit (lib.nvim.binds) addDescriptionsToMappings;
inherit (lib.nvim.attrsets) mapListToAttrs; inherit (lib.nvim.attrsets) mapListToAttrs;
lspKeyConfig = config.vim.lsp.mappings;
lspKeyOptions = options.vim.lsp.mappings;
mkLspBinding = optionName: action: let
key = lspKeyConfig.${optionName};
desc = lspKeyOptions.${optionName}.description;
in
optionalString (key != null) "vim.keymap.set('n', '${key}', ${action}, {buffer=bufnr, noremap=true, silent=true, desc='${desc}'})";
defaultServers = ["csharp_ls"]; defaultServers = ["csharp_ls"];
servers = { servers = ["csharp_ls" "omnisharp" "roslyn-ls"];
omnisharp = {
cmd = mkLuaInline ''
{
${toLuaObject (getExe pkgs.omnisharp-roslyn)},
'-z', -- https://github.com/OmniSharp/omnisharp-vscode/pull/4300
'--hostPID',
tostring(vim.fn.getpid()),
'DotNet:enablePackageRestore=false',
'--encoding',
'utf-8',
'--languageserver',
}
'';
filetypes = ["cs" "vb"];
root_dir = mkLuaInline ''
function(bufnr, on_dir)
local function find_root_pattern(fname, lua_pattern)
return vim.fs.root(0, function(name, path)
return name:match(lua_pattern)
end)
end
local fname = vim.api.nvim_buf_get_name(bufnr) defaultFormat = [];
on_dir(find_root_pattern(fname, "%.sln$") or find_root_pattern(fname, "%.csproj$")) formats = {
end csharpier = {
''; command = getExe pkgs.csharpier;
init_options = {};
capabilities = {
workspace = {
workspaceFolders = false; # https://github.com/OmniSharp/omnisharp-roslyn/issues/909
};
};
on_attach = mkLuaInline ''
function(client, bufnr)
local oe = require("omnisharp_extended")
${mkLspBinding "goToDefinition" "oe.lsp_definition"}
${mkLspBinding "goToType" "oe.lsp_type_definition"}
${mkLspBinding "listReferences" "oe.lsp_references"}
${mkLspBinding "listImplementations" "oe.lsp_implementation"}
end
'';
settings = {
FormattingOptions = {
# Enables support for reading code style, naming convention and analyzer
# settings from .editorconfig.
EnableEditorConfigSupport = true;
# Specifies whether 'using' directives should be grouped and sorted during
# document formatting.
OrganizeImports = null;
};
MsBuild = {
# If true, MSBuild project system will only load projects for files that
# were opened in the editor. This setting is useful for big C# codebases
# and allows for faster initialization of code navigation features only
# for projects that are relevant to code that is being edited. With this
# setting enabled OmniSharp may load fewer projects and may thus display
# incomplete reference lists for symbols.
LoadProjectsOnDemand = null;
};
RoslynExtensionsOptions = {
# Enables support for roslyn analyzers, code fixes and rulesets.
EnableAnalyzersSupport = null;
# Enables support for showing unimported types and unimported extension
# methods in completion lists. When committed, the appropriate using
# directive will be added at the top of the current file. This option can
# have a negative impact on initial completion responsiveness;
# particularly for the first few completion sessions after opening a
# solution.
EnableImportCompletion = null;
# Only run analyzers against open files when 'enableRoslynAnalyzers' is
# true
AnalyzeOpenDocumentsOnly = null;
# Enables the possibility to see the code in external nuget dependencies
EnableDecompilationSupport = null;
};
RenameOptions = {
RenameInComments = null;
RenameOverloads = null;
RenameInStrings = null;
};
Sdk = {
# Specifies whether to include preview versions of the .NET SDK when
# determining which version to use for project loading.
IncludePrereleases = true;
};
};
};
csharp_ls = {
cmd = [(lib.getExe pkgs.csharp-ls) "--features" "razor-support"];
filetypes = ["cs" "razor"];
root_dir = mkLuaInline ''
function(bufnr, on_dir)
local function find_root_pattern(fname, lua_pattern)
return vim.fs.root(0, function(name, path)
return name:match(lua_pattern)
end)
end
local fname = vim.api.nvim_buf_get_name(bufnr)
on_dir(find_root_pattern(fname, "%.sln$") or find_root_pattern(fname, "%.csproj$"))
end
'';
init_options = {
AutomaticWorkspaceInit = true;
};
};
roslyn_ls = {
cmd = mkLuaInline ''
{
${toLuaObject (getExe pkgs.roslyn-ls)},
'--logLevel=Warning',
'--extensionLogDirectory=' .. vim.fs.dirname(vim.lsp.get_log_path()),
'--stdio',
}
'';
filetypes = ["cs"];
root_dir = mkLuaInline ''
function(bufnr, on_dir)
local function find_root_pattern(fname, lua_pattern)
return vim.fs.root(0, function(name, path)
return name:match(lua_pattern)
end)
end
local fname = vim.api.nvim_buf_get_name(bufnr)
on_dir(find_root_pattern(fname, "%.sln$") or find_root_pattern(fname, "%.csproj$"))
end
'';
init_options = {};
};
roslyn = let
pkg = pkgs.vscode-extensions.ms-dotnettools.csharp;
pluginRoot = "${pkg}/share/vscode/extensions/ms-dotnettools.csharp";
exe = "${pluginRoot}/.roslyn/Microsoft.CodeAnalysis.LanguageServer";
razorSourceGenerator = "${pluginRoot}/.razorExtension/Microsoft.CodeAnalysis.LanguageServer";
razorDesignTimePath = "${pluginRoot}/.razorExtension/Targets/Microsoft.NET.Sdk.Razor.DesignTime.targets";
razorExtension = "${pluginRoot}/.razorExtension/Microsoft.VisualStudioCode.RazorExtension.dll";
in {
cmd = mkLuaInline ''
{
"dotnet",
"${exe}.dll",
"--stdio",
"--logLevel=Information",
"--extensionLogDirectory=" .. vim.fs.dirname(vim.lsp.get_log_path()),
"--razorSourceGenerator=${razorSourceGenerator}",
"--razorDesignTimePath=${razorDesignTimePath}",
"--extension=${razorExtension}",
}
'';
filetypes = ["cs" "razor"];
root_dir = mkLuaInline ''
function(bufnr, on_dir)
local function find_root_pattern(fname, lua_pattern)
return vim.fs.root(0, function(name, path)
return name:match(lua_pattern)
end)
end
local fname = vim.api.nvim_buf_get_name(bufnr)
on_dir(find_root_pattern(fname, "%.sln$") or find_root_pattern(fname, "%.csproj$"))
end
'';
init_options = {};
}; };
}; };
extraServerPlugins = { # Verbose names for clarity.
omnisharp = ["omnisharp-extended-lsp-nvim"]; shouldEnableExclusiveLspExtension = extension: lsp: cfg.lsp.enable && cfg.extensions.${extension}.enable && (elem lsp cfg.lsp.servers);
csharp_ls = ["csharpls-extended-lsp-nvim"]; mkAlertForMisuseOfExclusiveLspExtension = extension: lsp: (mkIf (cfg.lsp.enable
roslyn_ls = []; && cfg.extensions.${extension}.enable) {
roslyn = ["roslyn-nvim"]; assertion = elem lsp cfg.lsp.servers;
}; message = "${extension} requires ${lsp} to be listed in vim.languages.csharp.lsp.servers.";
});
cfg = config.vim.languages.csharp; cfg = config.vim.languages.csharp;
in { in {
@ -214,14 +45,14 @@ in {
::: {.note} ::: {.note}
This feature will not work if the .NET SDK is not installed. This feature will not work if the .NET SDK is not installed.
Both `roslyn` (with `roslyn-nvim`) and `csharp_ls` require the .NET SDK to function properly with Razor. Both `roslyn-ls` (with `roslyn-nvim`) and `csharp_ls` require the .NET SDK to function properly with Razor.
Ensure that the .NET SDK is installed. Ensure that the .NET SDK is installed.
Check for version compatibility for optimal performance. Check for version compatibility for optimal performance.
::: :::
::: {.warning} ::: {.warning}
At the moment, only `roslyn`(with roslyn-nvim) provides full Razor support. At the moment, only `roslyn-ls`(with roslyn-nvim) provides full Razor support.
`csharp_ls` is limited to `.cshtml` files. `csharp_ls` is limited to `.cshtml` files.
::: :::
''; '';
@ -229,13 +60,82 @@ in {
extensions = { extensions = {
roslyn-nvim = { roslyn-nvim = {
enable = mkEnableOption '' enable = mkEnableOption ''
Roslyn LSP plugin for neovim Roslyn LSP plugin for Neovim that adds Razor support and works with multiple solutions
::: {.note} ::: {.note}
This feature only works for `roslyn` (not `roslyn_ls`). This feature only works for `roslyn-ls`.
:::
'';
setupOpts = mkPluginSetupOption "roslyn-nvim" {
filewatching = mkOption {
description = ''
"auto" | "roslyn" | "off"
- "auto": Does nothing for filewatching, leaving everything as default
- "roslyn": Turns off neovim filewatching which will make roslyn do the filewatching
- "off": Hack to turn off all filewatching.
::: {.tip}
Set to "off" if you notice performance issues
:::
'';
type = enum ["auto" "roslyn" "off"];
default = "auto";
};
extensions.razor = {
enabled =
(mkEnableOption "Additional roslyn extensions (for example Roslynator/Razor)")
// {default = true;};
config = mkOption {
description = "Configuration for the additional roslyn extensions";
type = luaInline;
default = let
pkg = pkgs.vscode-extensions.ms-dotnettools.csharp;
pluginRoot = "${pkg}/share/vscode/extensions/ms-dotnettools.csharp";
razorExtension = "${pluginRoot}/.razorExtension/Microsoft.VisualStudioCode.RazorExtension.dll";
razorSourceGenerator = "${pluginRoot}/.razorExtension/Microsoft.CodeAnalysis.Razor.Compiler.dll";
razorDesignTimePath = "${pluginRoot}/.razorExtension/Targets/Microsoft.NET.Sdk.Razor.DesignTime.targets";
in
mkLuaInline ''
function()
return {
path = '${razorExtension}',
args = {
'--razorSourceGenerator=${razorSourceGenerator}',
'--razorDesignTimePath=${razorDesignTimePath}',
},
}
end
'';
};
};
};
};
omnisharp-extended-lsp-nvim = {
enable = mkEnableOption ''
Extended 'textDocument/definition' handler for OmniSharp Neovim LSP
::: {.note}
This feature only works for `omnisharp`.
:::
'';
mappings = let
inherit (config.vim.lsp) mappings;
in {
goToDefinition = mkMappingOption "Go to definition [omnisharp-extended-lsp-nvim]" mappings.goToDefinition;
goToType = mkMappingOption "Go to type [omnisharp-extended-lsp-nvim]" mappings.goToType;
listReferences = mkMappingOption "List references [omnisharp-extended-lsp-nvim]" mappings.listReferences;
listImplementations = mkMappingOption "List implementations [omnisharp-extended-lsp-nvim]" mappings.listImplementations;
};
};
csharpls-extended-lsp-nvim = {
enable = mkEnableOption ''
Extended 'textDocument/definition' handler for csharp_ls Neovim LSP
::: {.note}
This feature only works for `csharp_ls`.
::: :::
''; '';
setupOpts = mkPluginSetupOption "roslyn-nvim" {};
}; };
}; };
@ -259,10 +159,29 @@ in {
}; };
servers = mkOption { servers = mkOption {
description = "C# LSP server to use"; description = "C# LSP server to use";
type = deprecatedSingleOrListOf "vim.language.csharp.lsp.servers" (enum (attrNames servers)); type = listOf (enumWithRename
"vim.languages.csharp.lsp.servers"
servers {
roslyn_ls = "roslyn-ls";
});
default = defaultServers; default = defaultServers;
}; };
}; };
format = {
enable =
mkEnableOption "C# formatting"
// {
default = config.vim.languages.enableFormat;
defaultText = literalExpression "config.vim.languages.enableFormat";
};
type = mkOption {
description = "C# formatter to use";
type = listOf (enum (attrNames formats));
default = defaultFormat;
};
};
}; };
}; };
@ -274,34 +193,86 @@ in {
(mkIf cfg.lsp.enable { (mkIf cfg.lsp.enable {
vim = { vim = {
startPlugins = concatMap (server: extraServerPlugins.${server}) cfg.lsp.servers; luaConfigRC.razorFileTypes = ''
luaConfigRC.razorFileTypes = -- Set unknown file types!
/* vim.filetype.add {
lua extension = {
*/ razor = "razor",
'' cshtml = "razor",
-- Set unknown file types! },
vim.filetype.add { }
extension = { '';
razor = "razor", lsp = {
cshtml = "razor", presets = genAttrs cfg.lsp.servers (_: {enable = true;});
}, servers = genAttrs cfg.lsp.servers (_: {
} filetypes = ["cs" "razor" "vb"];
''; });
lsp.servers = };
mapListToAttrs (name: {
inherit name;
value = servers.${name};
})
cfg.lsp.servers;
}; };
}) })
(mkIf cfg.extensions.roslyn-nvim.enable {
vim = mkMerge [ (mkIf cfg.format.enable {
{ vim.formatter.conform-nvim = {
startPlugins = ["roslyn-nvim"]; enable = true;
} setupOpts = {
formatters_by_ft.cs = cfg.format.type;
formatters =
mapListToAttrs (name: {
inherit name;
value = formats.${name};
})
(filter (name: name != "lsp") cfg.format.type);
};
};
})
{
assertions = [
(mkAlertForMisuseOfExclusiveLspExtension "roslyn-nvim" "roslyn-ls")
(mkAlertForMisuseOfExclusiveLspExtension "csharpls-extended-lsp-nvim" "csharp_ls")
(mkAlertForMisuseOfExclusiveLspExtension "omnisharp-extended-lsp-nvim" "omnisharp")
]; ];
}
(mkIf (shouldEnableExclusiveLspExtension "roslyn-nvim" "roslyn-ls") {
vim = {
startPlugins = ["roslyn-nvim"];
pluginRC.roslyn-nvim = entryAnywhere "require('roslyn').setup(${toLuaObject cfg.extensions.roslyn-nvim.setupOpts})";
lsp.servers.roslyn-ls.enable = false;
extraPackages = with pkgs; [roslyn-ls];
};
})
(mkIf (shouldEnableExclusiveLspExtension "omnisharp-extended-lsp-nvim" "omnisharp") {
vim = {
startPlugins = ["omnisharp-extended-lsp-nvim"];
lsp.servers.omnisharp.on_attach = let
mappingDefinitions = options.vim.languages.csharp.extensions.omnisharp-extended-lsp-nvim.mappings;
mappings = addDescriptionsToMappings cfg.extensions.omnisharp-extended-lsp-nvim.mappings mappingDefinitions;
mkBinding = binding: action:
if binding.value != null
then "vim.keymap.set('n', ${toLuaObject binding.value}, ${action}, {buffer=bufnr, noremap=true, silent=true, desc=${toLuaObject binding.description}})"
else "";
in
mkLuaInline
''
function(client, bufnr)
${mkBinding mappings.goToDefinition "require('omnisharp_extended').lsp_definition"}
${mkBinding mappings.goToType "require('omnisharp_extended').lsp_type_definition"}
${mkBinding mappings.listReferences "require('omnisharp_extended').lsp_references"}
${mkBinding mappings.listImplementations "require('omnisharp_extended').lsp_implementation"}
end
'';
};
})
(mkIf (shouldEnableExclusiveLspExtension "csharpls-extended-lsp-nvim" "csharp_ls") {
vim = {
startPlugins = ["csharpls-extended-lsp-nvim"];
lsp.servers.csharp_ls.on_attach = mkLuaInline ''
function(client, bufnr)
require('csharpls_extended').buf_read_cmd_bind()
end
'';
};
}) })
]); ]);
} }

View file

@ -0,0 +1,51 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib.meta) getExe;
inherit (lib.modules) mkIf;
inherit (lib.nvim.types) mkLspPresetEnableOption;
inherit (lib.generators) mkLuaInline;
cfg = config.vim.lsp.presets.csharp_ls;
in {
# HACK: this server should be named `csharp-ls`, but the extension `csharpls-extended-lsp-nvim` only works if it is named `csharp_ls`
options.vim.lsp.presets.csharp_ls = {
enable = mkLspPresetEnableOption "csharp_ls" "C#" [];
};
config = mkIf cfg.enable {
vim.lsp.servers.csharp_ls = {
cmd = mkLuaInline ''
function(dispatchers, config)
return vim.lsp.rpc.start({ '${getExe pkgs.csharp-ls}', '--features', 'razor-support' , '--features', 'metadata-uris'}, dispatchers, {
-- csharp-ls attempt to locate sln, slnx or csproj files from cwd, so set cwd to root directory.
-- If cmd_cwd is provided, use it instead.
cwd = config.cmd_cwd or config.root_dir,
env = config.cmd_env,
detached = config.detached,
})
end
'';
root_dir = mkLuaInline ''
function(bufnr, on_dir)
local fname = vim.api.nvim_buf_get_name(bufnr)
on_dir(util.root_pattern '*.sln'(fname) or util.root_pattern '*.slnx'(fname) or util.root_pattern '*.csproj'(fname))
end
'';
init_options = {
AutomaticWorkspaceInit = true;
};
get_language_id = mkLuaInline ''
function(_, ft)
if ft == 'cs' then
return 'csharp'
end
return ft
end
'';
};
};
}

View file

@ -9,6 +9,7 @@
./ccls.nix ./ccls.nix
./clangd.nix ./clangd.nix
./clojure-lsp.nix ./clojure-lsp.nix
./csharp_ls.nix
./cue.nix ./cue.nix
./dart.nix ./dart.nix
./deno.nix ./deno.nix
@ -39,6 +40,7 @@
./nushell.nix ./nushell.nix
./ocaml-lsp.nix ./ocaml-lsp.nix
./ols.nix ./ols.nix
./omnisharp.nix
./openscad-lsp.nix ./openscad-lsp.nix
./phan.nix ./phan.nix
./phpactor.nix ./phpactor.nix
@ -47,6 +49,7 @@
./python-lsp-server.nix ./python-lsp-server.nix
./qmlls.nix ./qmlls.nix
./r-languageserver.nix ./r-languageserver.nix
./roslyn-ls.nix
./ruby-lsp.nix ./ruby-lsp.nix
./ruff.nix ./ruff.nix
./rumdl.nix ./rumdl.nix

View file

@ -0,0 +1,98 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib.meta) getExe;
inherit (lib.modules) mkIf;
inherit (lib.nvim.types) mkLspPresetEnableOption;
inherit (lib.generators) mkLuaInline;
cfg = config.vim.lsp.presets.omnisharp;
in {
# HACK: this server should be named `omnisharp-roslyn`, but the extension `omnisharp-extended-lsp-nvim` only works if it is named `omnisharp`
options.vim.lsp.presets.omnisharp = {
enable = mkLspPresetEnableOption "omnisharp" "OmniSharp Roslyn" [];
};
config = mkIf cfg.enable {
vim.lsp.servers.omnisharp = {
cmd = mkLuaInline ''
{
'${getExe pkgs.omnisharp-roslyn}',
'-z', -- https://github.com/OmniSharp/omnisharp-vscode/pull/4300
'--hostPID',
tostring(vim.fn.getpid()),
'DotNet:enablePackageRestore=false',
'--encoding',
'utf-8',
'--languageserver',
}
'';
root_dir = mkLuaInline ''
function(bufnr, on_dir)
local fname = vim.api.nvim_buf_get_name(bufnr)
on_dir(
util.root_pattern '*.slnx'(fname)
or util.root_pattern '*.sln'(fname)
or util.root_pattern '*.csproj'(fname)
or util.root_pattern 'omnisharp.json'(fname)
or util.root_pattern 'function.json'(fname)
)
end
'';
init_options = {};
capabilities = {
workspace = {
workspaceFolders = false; # https://github.com/OmniSharp/omnisharp-roslyn/issues/909
};
};
settings = {
FormattingOptions = {
# Enables support for reading code style, naming convention and analyzer
# settings from .editorconfig.
EnableEditorConfigSupport = true;
# Specifies whether 'using' directives should be grouped and sorted during
# document formatting.
OrganizeImports = null;
};
MsBuild = {
# If true, MSBuild project system will only load projects for files that
# were opened in the editor. This setting is useful for big C# codebases
# and allows for faster initialization of code navigation features only
# for projects that are relevant to code that is being edited. With this
# setting enabled OmniSharp may load fewer projects and may thus display
# incomplete reference lists for symbols.
LoadProjectsOnDemand = null;
};
RoslynExtensionsOptions = {
# Enables support for roslyn analyzers, code fixes and rulesets.
EnableAnalyzersSupport = null;
# Enables support for showing unimported types and unimported extension
# methods in completion lists. When committed, the appropriate using
# directive will be added at the top of the current file. This option can
# have a negative impact on initial completion responsiveness;
# particularly for the first few completion sessions after opening a
# solution.
EnableImportCompletion = null;
# Only run analyzers against open files when 'enableRoslynAnalyzers' is
# true
AnalyzeOpenDocumentsOnly = null;
# Enables the possibility to see the code in external nuget dependencies
EnableDecompilationSupport = null;
};
RenameOptions = {
RenameInComments = null;
RenameOverloads = null;
RenameInStrings = null;
};
Sdk = {
# Specifies whether to include preview versions of the .NET SDK when
# determining which version to use for project loading.
IncludePrereleases = true;
};
};
};
};
}

View file

@ -0,0 +1,402 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib.meta) getExe;
inherit (lib.modules) mkIf;
inherit (lib.nvim.types) mkLspPresetEnableOption;
inherit (lib.generators) mkLuaInline;
cfg = config.vim.lsp.presets.roslyn-ls;
in {
options.vim.lsp.presets.roslyn-ls = {
enable = mkLspPresetEnableOption "roslyn-ls" "Roslyn" [];
};
config = mkIf cfg.enable {
vim.lsp.servers.roslyn-ls = {
cmd = mkLuaInline ''
{
'${getExe pkgs.roslyn-ls}',
'--logLevel',
'Information',
'--extensionLogDirectory',
vim.fs.joinpath(vim.uv.os_tmpdir(), 'roslyn_ls/logs'),
'--stdio',
}
'';
cmd_env = mkLuaInline ''
{
-- Fixes LSP navigation in decompiled files for systems with symlinked TMPDIR (macOS)
TMPDIR = vim.env.TMPDIR and vim.env.TMPDIR ~= "" and vim.fn.resolve(vim.env.TMPDIR) or nil,
}
'';
handlers = {
"workspace/projectInitializationComplete" = mkLuaInline ''
function(_, _, ctx)
vim.notify('Roslyn project initialization complete', vim.log.levels.INFO, { title = 'roslyn_ls' })
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
local function refresh_diagnostics(client)
for buf, _ in pairs(vim.lsp.get_client_by_id(client.id).attached_buffers) do
if vim.api.nvim_buf_is_loaded(buf) then
client:request(
vim.lsp.protocol.Methods.textDocument_diagnostic,
{ textDocument = vim.lsp.util.make_text_document_params(buf) },
nil,
buf
)
end
end
end
refresh_diagnostics(client)
return vim.NIL
end
'';
"workspace/_roslyn_projectNeedsRestore" = mkLuaInline ''
function(_, result, ctx)
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
---@diagnostic disable-next-line: param-type-mismatch
client:request('workspace/_roslyn_restore', result, function(err, response)
if err then
vim.notify(err.message, vim.log.levels.ERROR, { title = 'roslyn_ls' })
end
if response then
for _, v in ipairs(response) do
vim.notify(v.message, vim.log.levels.INFO, { title = 'roslyn_ls' })
end
end
end)
return vim.NIL
end
'';
"razor/provideDynamicFileInfo" = mkLuaInline ''
function(_, _, _)
vim.notify(
'Razor is not supported.\nPlease use https://github.com/tris203/rzls.nvim',
vim.log.levels.WARN,
{ title = 'roslyn_ls' }
)
return vim.NIL
end
'';
};
commands = {
"roslyn.client.completionComplexEdit" = mkLuaInline ''
function(command, ctx)
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
local args = command.arguments or {}
local uri, edit = args[1], args[2]
---@diagnostic disable: undefined-field
if uri and edit and edit.newText and edit.range then
local workspace_edit = {
changes = {
[uri.uri] = {
{
range = edit.range,
newText = edit.newText,
},
},
},
}
vim.lsp.util.apply_workspace_edit(workspace_edit, client.offset_encoding)
---@diagnostic enable: undefined-field
else
vim.notify('roslyn_ls: completionComplexEdit args not understood: ' .. vim.inspect(args), vim.log.levels.WARN)
end
end
'';
"roslyn.client.nestedCodeAction" = mkLuaInline ''
function(command, ctx)
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
local arg = command.arguments and command.arguments[1]
if type(arg) ~= 'table' then
vim.notify('roslyn_ls: invalid nestedCodeAction arguments', vim.log.levels.ERROR)
return
end
local function handle(action)
if not action then
return
end
if action.data and not action.edit and not action.command then
client:request('codeAction/resolve', action, function(err, resolved)
if err then
vim.notify(err.message or tostring(err), vim.log.levels.ERROR)
return
end
if resolved then
handle(resolved)
end
end, ctx.bufnr)
return
end
local nested = vim.islist(action) and action or action.NestedCodeActions
if type(nested) ~= 'table' or vim.tbl_isempty(nested) then
local function apply_action(client, action)
if action.edit then
vim.lsp.util.apply_workspace_edit(action.edit, client.offset_encoding)
end
if action.command then
client:exec_cmd(action.command)
end
end
apply_action(client, action)
return
end
if #nested == 1 then
handle(nested[1])
return
end
vim.ui.select(nested, {
prompt = action.title or 'Select code action',
format_item = function(item)
return item.title or (item.command and item.command.title) or 'Unnamed action'
end,
}, function(choice)
if choice then
handle(choice)
end
end)
end
handle(arg)
end
'';
"roslyn.client.fixAllCodeAction" = mkLuaInline ''
function(command, ctx)
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
local function handle_fix_all_action(client, command, bufnr)
local arg = command.arguments and command.arguments[1]
if type(arg) ~= 'table' then
vim.notify('roslyn_ls: invalid fixAllCodeAction arguments', vim.log.levels.ERROR)
return
end
local flavors = arg.FixAllFlavors
if type(flavors) ~= 'table' or vim.tbl_isempty(flavors) then
vim.notify('roslyn_ls: fixAllCodeAction has no FixAllFlavors', vim.log.levels.WARN)
return
end
vim.ui.select(flavors, {
prompt = 'Fix All Scope:',
}, function(chosen_scope)
if not chosen_scope then
return
end
client:request('codeAction/resolveFixAll', {
title = command.title,
data = arg,
scope = chosen_scope,
}, function(err, resolved)
if err then
vim.notify(
'roslyn_ls: fixAllCodeAction resolve error: ' .. (err.message or tostring(err)),
vim.log.levels.ERROR
)
return
end
if resolved then
local function apply_action(client, action)
if action.edit then
vim.lsp.util.apply_workspace_edit(action.edit, client.offset_encoding)
end
if action.command then
client:exec_cmd(action.command)
end
end
apply_action(client, resolved)
end
end, bufnr)
end)
end
handle_fix_all_action(client, command, ctx.bufnr)
end
'';
};
root_dir = mkLuaInline ''
function(bufnr, cb)
local bufname = vim.api.nvim_buf_get_name(bufnr)
local function is_decompiled(bufname)
local _, endpos = bufname:find('[/\\]MetadataAsSource[/\\]')
if endpos == nil then
return false
end
return vim.fn.finddir(bufname:sub(1, endpos), vim.uv.os_tmpdir()) ~= ""
end
-- don't try to find sln or csproj for files from libraries
-- outside of the project
if not is_decompiled(bufname) then
-- try find solutions root first
local root_dir = vim.fs.root(bufnr, function(fname, _)
return fname:match('%.sln[x]?$') ~= nil
end)
if not root_dir then
-- try find projects root
root_dir = vim.fs.root(bufnr, function(fname, _)
return fname:match('%.csproj$') ~= nil
end)
end
if root_dir then
cb(root_dir)
end
else
-- Decompiled code (example: "/tmp/MetadataAsSource/f2bfba/DecompilationMetadataAsSourceFileProvider/d5782a/Console.cs")
local prev_buf = vim.fn.bufnr('#')
local client = vim.lsp.get_clients({
name = 'roslyn_ls',
bufnr = prev_buf ~= 1 and prev_buf or nil,
})[1]
if client then
cb(client.config.root_dir)
end
end
end
'';
on_init = [
(mkLuaInline
''
function(client)
local root_dir = client.config.root_dir
local function on_init_sln(client, target)
vim.notify('Initializing: ' .. target, vim.log.levels.TRACE, { title = 'roslyn_ls' })
---@diagnostic disable-next-line: param-type-mismatch
client:notify('solution/open', {
solution = vim.uri_from_fname(target),
})
end
local function on_init_project(client, project_files)
vim.notify('Initializing: projects', vim.log.levels.TRACE, { title = 'roslyn_ls' })
---@diagnostic disable-next-line: param-type-mismatch
client:notify('project/open', {
projects = vim.tbl_map(function(file)
return vim.uri_from_fname(file)
end, project_files),
})
end
-- try load first solution we find
for entry, type in vim.fs.dir(root_dir) do
if type == 'file' and (vim.endswith(entry, '.sln') or vim.endswith(entry, '.slnx')) then
on_init_sln(client, vim.fs.joinpath(root_dir, entry))
return
end
end
-- if no solution is found load project
for entry, type in vim.fs.dir(root_dir) do
if type == 'file' and vim.endswith(entry, '.csproj') then
on_init_project(client, { vim.fs.joinpath(root_dir, entry) })
end
end
end
'')
];
on_attach = mkLuaInline ''
function(client, bufnr)
-- avoid duplicate autocmds for same buffer
if vim.api.nvim_get_autocmds({ buffer = bufnr, group = group })[1] then
return
end
local function refresh_diagnostics(client)
for buf, _ in pairs(vim.lsp.get_client_by_id(client.id).attached_buffers) do
if vim.api.nvim_buf_is_loaded(buf) then
client:request(
vim.lsp.protocol.Methods.textDocument_diagnostic,
{ textDocument = vim.lsp.util.make_text_document_params(buf) },
nil,
buf
)
end
end
end
vim.api.nvim_create_autocmd({ 'BufWritePost', 'InsertLeave' }, {
group = group,
buffer = bufnr,
callback = function()
local function refresh_diagnostics(client)
for buf, _ in pairs(vim.lsp.get_client_by_id(client.id).attached_buffers) do
if vim.api.nvim_buf_is_loaded(buf) then
client:request(
vim.lsp.protocol.Methods.textDocument_diagnostic,
{ textDocument = vim.lsp.util.make_text_document_params(buf) },
nil,
buf
)
end
end
end
refresh_diagnostics(client)
end,
desc = 'roslyn_ls: refresh diagnostics',
})
end
'';
capabilities = {
# HACK: Doesn't show any diagnostics if we do not set this to true
textDocument = {
diagnostic = {
dynamicRegistration = true;
};
};
};
settings = {
"csharp|background_analysis" = {
dotnet_analyzer_diagnostics_scope = "fullSolution";
dotnet_compiler_diagnostics_scope = "fullSolution";
};
"csharp|inlay_hints" = {
csharp_enable_inlay_hints_for_implicit_object_creation = true;
csharp_enable_inlay_hints_for_implicit_variable_types = true;
csharp_enable_inlay_hints_for_lambda_parameter_types = true;
csharp_enable_inlay_hints_for_types = true;
dotnet_enable_inlay_hints_for_indexer_parameters = true;
dotnet_enable_inlay_hints_for_literal_parameters = true;
dotnet_enable_inlay_hints_for_object_creation_parameters = true;
dotnet_enable_inlay_hints_for_other_parameters = true;
dotnet_enable_inlay_hints_for_parameters = true;
dotnet_suppress_inlay_hints_for_parameters_that_differ_only_by_suffix = true;
dotnet_suppress_inlay_hints_for_parameters_that_match_argument_name = true;
dotnet_suppress_inlay_hints_for_parameters_that_match_method_intent = true;
};
"csharp|symbol_search" = {
dotnet_search_reference_assemblies = true;
};
"csharp|completion" = {
dotnet_show_name_completion_suggestions = true;
dotnet_show_completion_items_from_unimported_namespaces = true;
dotnet_provide_regex_completions = true;
};
"csharp|code_lens" = {
dotnet_enable_references_code_lens = true;
};
};
};
};
}

View file

@ -2524,9 +2524,9 @@
}, },
"branch": "main", "branch": "main",
"submodules": false, "submodules": false,
"revision": "24f7c91ee5e09c63104deaab68f932620f25c24a", "revision": "946e559db19bfc5c7bf8be524a9c1acfde8ffffe",
"url": "https://github.com/seblyng/roslyn.nvim/archive/24f7c91ee5e09c63104deaab68f932620f25c24a.tar.gz", "url": "https://github.com/seblyng/roslyn.nvim/archive/946e559db19bfc5c7bf8be524a9c1acfde8ffffe.tar.gz",
"hash": "sha256-a/Slmkrz/4P/rfRhPa1W5kGV7joQNTN0Un7bbncCnk0=" "hash": "sha256-pkWZ9z2eLnaVUAjTaj1+AdgqR1GLY+RT6pxbpe50SOU="
}, },
"rtp-nvim": { "rtp-nvim": {
"type": "Git", "type": "Git",