Skip to main

AK#Notes

NeoVim LSP Setup

.lua
-- LSP
-- ============================================================================

local function open_definition_in_split()
    -- Get the current buffer's LSP clients
    local clients = vim.lsp.get_clients({ bufnr = 0 })
    if #clients == 0 then
        print("No LSP client attached to this buffer")
        return
    end

    -- Use the first client to determine the position encoding
    local first_client = clients[1]
    local position_encoding = first_client.offset_encoding or "utf-16"

    -- Create position parameters with the correct encoding
    local params = vim.lsp.util.make_position_params(vim.api.nvim_get_current_win(), position_encoding)

    -- Request the definition from the LSP
    local result = vim.lsp.buf_request_sync(0, "textDocument/definition", params, 1000)

    -- Handle errors or empty results
    if not result or vim.tbl_isempty(result) then
        print("No definition found")
        return
    end

    -- Extract the first location from the LSP result
    for _, client_result in pairs(result) do
        if client_result.result and not vim.tbl_isempty(client_result.result) then
            print(client_result.result)
            local location = client_result.result[1]
            local uri = location.targetUri or location.uri
            local range = location.targetRange or location.range
            local file = vim.uri_to_fname(uri)
            local line = range.start.line + 1  -- LSP lines are 0-based, Neovim is 1-based
            local col = range.start.character  -- 0-based columns

            -- Get the current window and list of windows in the current tab
            local current_win = vim.api.nvim_get_current_win()
            local tab_wins = vim.api.nvim_tabpage_list_wins(0)
            local other_wins = vim.tbl_filter(function(w) return w ~= current_win end, tab_wins)

            -- If there’s an existing split, use it and move the cursor there; otherwise, use the current window
            if #other_wins > 0 then
                local target_win = other_wins[1]
                vim.api.nvim_win_call(target_win, function()
                    vim.cmd('edit ' .. file)
                    vim.api.nvim_win_set_cursor(0, {line, col})
                end)
                vim.api.nvim_set_current_win(target_win)  -- Move cursor to the target split
            else
                vim.cmd('edit ' .. file)
                vim.api.nvim_win_set_cursor(0, {line, col})
            end
            return  -- Exit after handling the first valid result
        end
    end
    print("No definition found from any client")
end

vim.keymap.set('n', '<leader>sd', open_definition_in_split, { noremap = true, silent = true, desc = "Go to [S]plit [D]efinition" })
-- vim.keymap.set('n', '<leader>sc', open_declaration_in_split, { noremap = true, silent = true, desc = "Go to [S]plit [D]efinition" })

local group = vim.api.nvim_create_augroup("lsp_document_highlight", { clear = true })
vim.api.nvim_create_autocmd('LspAttach', {
    callback = function(args)
        -- LSP Keymap
        vim.keymap.set('n', 'grr', require('telescope.builtin').lsp_references, { desc = '[G]oto [R]eferences' })
        vim.keymap.set('n', ']c', vim.lsp.buf.declaration,                      { desc = 'Jump to de[C]laration' })
        -- vim.keymap.set('n', '<leader>sc', function()
        --     vim.cmd('wincmd w')
        --     vim.lsp.buf.declaration()
        -- end, { desc = '[S]plit de[C]laration' })
        vim.keymap.set('n', '<leader>fs', require('telescope.builtin').lsp_document_symbols,          { desc = 'Open Document Symbols' })
        vim.keymap.set('n', '<leader>fw', require('telescope.builtin').lsp_dynamic_workspace_symbols, { desc = 'Open Workspace Symbols' })

        -- LSP Highlight
        vim.api.nvim_create_autocmd("CursorHold", {
            group = group,
            buffer = 0,
            callback = vim.lsp.buf.document_highlight,
            desc = "Highlight document symbols under cursor",
        })
        vim.api.nvim_create_autocmd("CursorHoldI", {
            group = group,
            buffer = 0,
            callback = vim.lsp.buf.document_highlight,
            desc = "Highlight document symbols under cursor (insert mode)",
        })
        vim.api.nvim_create_autocmd("CursorMoved", {
            group = group,
            buffer = 0,
            callback = vim.lsp.buf.clear_references,
            desc = "Clear document highlights on cursor move",
        })
    end,
})

vim.lsp.handlers["textDocument/publishDiagnostics"] = function() end
vim.lsp.enable('clangd')
vim.lsp.config('clangd', {
    cmd = { vim.fn.expand("~/Code/Tools/clangd/bin/clangd") },
    filetypes = { 'c', 'cpp' },
    root_markers = { 'build.c' },
})