Skip to content
Sebastian Lyng Johansen edited this page Dec 5, 2024 · 7 revisions

Tips and tricks

I ideally want this plugin to be pretty minimal, and mostly just a wrapper to start the roslyn server. I am not envisioning some big dev kit like vscode's plugin C# dev kit. I might add some custom handlers for the roslyn server, but I don't want to add everything.

This will be a wiki with some tips and tricks that I will probably update if there is some requests that I don't want to add to this plugin, but instead think the users can add to their config if they want this

Diagnostic refresh

Currently, the diagnostics doesn't appear before making some changes or going into insert mode. (I am not 100% which events triggers this). However, it is possible to retrieve them as often as you would like with something like this:

vim.api.nvim_create_autocmd({ "InsertLeave" }, {
    pattern = "*",
    callback = function()
        local clients = vim.lsp.get_clients({ name = "roslyn" })
        if not clients or #clients == 0 then
            return
        end

        local buffers = vim.lsp.get_buffers_by_client_id(clients[1].id)
        for _, buf in ipairs(buffers) do
            vim.lsp.util._refresh("textDocument/diagnostic", { bufnr = buf })
        end
    end,
})

Just change the InsertLeave event to whichever events you would like

Remove unnecessary using directives

This is already provided with the code actions if you have the cursor over the using statements. I therefore will not add a special command for this. If you however want this, you could do something like this:

vim.api.nvim_create_user_command("CSFixUsings", function()
    local bufnr = vim.api.nvim_get_current_buf()

    local clients = vim.lsp.get_clients({ name = "roslyn" })
    if not clients or vim.tbl_isempty(clients) then
        vim.notify("Couldn't find client", vim.log.levels.ERROR, { title = "Roslyn" })
        return
    end

    local client = clients[1]
    local action = {
        kind = "quickfix",
        data = {
            CustomTags = { "RemoveUnnecessaryImports" },
            TextDocument = { uri = vim.uri_from_bufnr(bufnr) },
            CodeActionPath = { "Remove unnecessary usings" },
            Range = {
                ["start"] = { line = 0, character = 0 },
                ["end"] = { line = 0, character = 0 },
            },
            UniqueIdentifier = "Remove unnecessary usings",
        },
    }

    client.request("codeAction/resolve", action, function(err, resolved_action)
        if err then
            vim.notify("Fix using directives failed", vim.log.levels.ERROR, { title = "Roslyn" })
            return
        end
        vim.lsp.util.apply_workspace_edit(resolved_action.edit, client.offset_encoding)
    end)
end, { desc = "Remove unnecessary using directives" })

Semantic tokens

Neovim doesn't support semantic tokens for the full buffer. It only supports it for a range as of writing. This is possible to "hack" to make it work for the full buffer, making semantic tokens work. This is not a part of this plugin, but I am considering making this an opt-in. But for now, you can copy this method and call it from on_attach. I cannot guarantee that this works exactly as expected, as this is a hack. There is multiple snippets here: https://github.com/seblj/roslyn.nvim/issues/11 in case this example does not work as expected

--- @param client vim.lsp.Client the LSP client
local function monkey_patch_semantic_tokens(client)
    -- NOTE: Super hacky... Don't know if I like that we set a random variable on
    -- the client Seems to work though ~seblj
    if client.is_hacked then
        return
    end
    client.is_hacked = true

    -- let the runtime know the server can do semanticTokens/full now
    client.server_capabilities = vim.tbl_deep_extend("force", client.server_capabilities, {
        semanticTokensProvider = {
            full = true,
        },
    })

    -- monkey patch the request proxy
    local request_inner = client.request
    function client:request(method, params, handler, req_bufnr)
        if method ~= vim.lsp.protocol.Methods.textDocument_semanticTokens_full then
            return request_inner(self, method, params, handler)
        end

        local target_bufnr = vim.uri_to_bufnr(params.textDocument.uri)
        local line_count = vim.api.nvim_buf_line_count(target_bufnr)
        local last_line = vim.api.nvim_buf_get_lines(target_bufnr, line_count - 1, line_count, true)[1]

        return request_inner(self, "textDocument/semanticTokens/range", {
            textDocument = params.textDocument,
            range = {
                ["start"] = {
                    line = 0,
                    character = 0,
                },
                ["end"] = {
                    line = line_count - 1,
                    character = string.len(last_line) - 1,
                },
            },
        }, handler, req_bufnr)
    end
end
Clone this wiki locally