Skip to content

Two definitions for local function variables #2451

Open

Description

@tobias-haenel

How are you using the lua-language-server?

NeoVim

Which OS are you using?

Linux

What is the issue affecting?

Other

Expected Behaviour

Using vim.lsp.buf.definition() to go to the definition of a local function variable (assigned with the = operator), should give only a single definition at the local variable name.

Actual Behaviour

Using vim.lsp.buf.definition() to go to the definition of a local function variable (assigned with the = operator), gives two definitions on the same line. One is located at the variable name and the other is located at the beginning of the function key word.

Reproduction steps

  1. Create a lua file with the following content:
local m = function() print "hello world" end

m()
  1. Move the cursor on the m() function call
  2. Execute the neovim command :lua vim.lsp.buf.definition()

Additional Notes

No response

Log File

[DEBUG][2023-12-16 17:31:59] .../lua/vim/lsp.lua:1391	"LSP[lua_ls]"	"client.request"	1	"textDocument/definition"	{  position = {    character = 0,    line = 2  },  textDocument = {    uri = "file:///home/tobias/.config/nvim/test.lua"  }}	<function 1>	147
[DEBUG][2023-12-16 17:31:59] .../vim/lsp/rpc.lua:284	"rpc.send"	{  id = 1559,  jsonrpc = "2.0",  method = "textDocument/definition",  params = {    position = {      character = 0,      line = 2    },    textDocument = {      uri = "file:///home/tobias/.config/nvim/test.lua"    }  }}
[DEBUG][2023-12-16 17:31:59] .../vim/lsp/rpc.lua:387	"rpc.receive"	{  id = 1559,  jsonrpc = "2.0",  result = { {      originSelectionRange = {        ["end"] = {          character = 1,          line = 2        },        start = {          character = 0,          line = 2        }      },      targetRange = {        ["end"] = {          character = 7,          line = 0        },        start = {          character = 6,          line = 0        }      },      targetSelectionRange = {        ["end"] = {          character = 7,          line = 0        },        start = {          character = 6,          line = 0        }      },      targetUri = "file:///home/tobias/.config/nvim/test.lua"    }, {      originSelectionRange = {        ["end"] = {          character = 1,          line = 2        },        start = {          character = 0,          line = 2        }      },      targetRange = {        ["end"] = {          character = 44,          line = 0        },        start = {          character = 10,          line = 0        }      },      targetSelectionRange = {        ["end"] = {          character = 44,          line = 0        },        start = {          character = 10,          line = 0        }      },      targetUri = "file:///home/tobias/.config/nvim/test.lua"    } }}
[DEBUG][2023-12-16 17:32:02] .../lua/vim/lsp.lua:1391	"LSP[lua_ls]"	"client.request"	1	"textDocument/codeLens"	{  textDocument = {    uri = "file:///home/tobias/.config/nvim/test.lua"  }}	<function 1>	147
[DEBUG][2023-12-16 17:32:02] .../vim/lsp/rpc.lua:284	"rpc.send"	{  id = 1560,  jsonrpc = "2.0",  method = "textDocument/codeLens",  params = {    textDocument = {      uri = "file:///home/tobias/.config/nvim/test.lua"    }  }}
[DEBUG][2023-12-16 17:32:02] .../vim/lsp/rpc.lua:387	"rpc.receive"	{  id = 1560,  jsonrpc = "2.0"}
[DEBUG][2023-12-16 17:32:02] .../lua/vim/lsp.lua:1391	"LSP[lua_ls]"	"client.request"	1	"textDocument/documentHighlight"	{  position = {    character = 0,    line = 2  },  textDocument = {    uri = "file:///home/tobias/.config/nvim/test.lua"  }}	<function 1>	147
[DEBUG][2023-12-16 17:32:02] .../vim/lsp/rpc.lua:284	"rpc.send"	{  id = 1561,  jsonrpc = "2.0",  method = "textDocument/documentHighlight",  params = {    position = {      character = 0,      line = 2    },    textDocument = {      uri = "file:///home/tobias/.config/nvim/test.lua"    }  }}
[DEBUG][2023-12-16 17:32:02] .../vim/lsp/rpc.lua:387	"rpc.receive"	{  id = 1561,  jsonrpc = "2.0",  result = { {      kind = 3,      range = {        ["end"] = {          character = 7,          line = 0        },        start = {          character = 6,          line = 0        }      }    }, {      kind = 2,      range = {        ["end"] = {          character = 1,          line = 2        },        start = {          character = 0,          line = 2        }      }    } }}
[TRACE][2023-12-16 17:32:02] ...lsp/handlers.lua:618	"default_handler"	"textDocument/documentHighlight"	{  ctx = '{\n  bufnr = 147,\n  client_id = 1,\n  method = "textDocument/documentHighlight",\n  params = {\n    position = {\n      character = 0,\n      line = 2\n    },\n    textDocument = {\n      uri = "file:///home/tobias/.config/nvim/test.lua"\n    }\n  }\n}',  result = { {      kind = 3,      range = {        ["end"] = {          character = 7,          line = 0        },        start = {          character = 6,          line = 0        }      }    }, {      kind = 2,      range = {        ["end"] = {          character = 1,          line = 2        },        start = {          character = 0,          line = 2        }      }    } }}

Activity

  1. eanyanwu commented on Jan 15, 2024

    @eanyanwu

    I have also been experiencing this. Seems very similar to this old ticket.

  2. rish987 commented on Feb 17, 2024

    @rish987

    For neovim users, here's a monkey-patch you can add to your config that should let you get around this until a fix comes around from the lua-language-server side:

    local locations_to_items = vim.lsp.util.locations_to_items
    vim.lsp.util.locations_to_items = function (locations, offset_encoding)
      local lines = {}
      local loc_i = 1
      for _, loc in ipairs(vim.deepcopy(locations)) do
        local uri = loc.uri or loc.targetUri
        local range = loc.range or loc.targetSelectionRange
        if lines[uri .. range.start.line] then -- already have a location on this line
          table.remove(locations, loc_i) -- remove from the original list
        else
          loc_i = loc_i + 1
        end
        lines[uri .. range.start.line] = true
      end
    
      return locations_to_items(locations, offset_encoding)
    end
  3. barrettruth commented on Jan 13, 2026

    @barrettruth

    @tobias-haenel could you please offer some clarification as to why this was closed? still occurring in 2026 and seems like a pretty reasonable bug fix.

    also pinging @PeterCardenas for hopefully a status update on the fix?

  4. tobias-haenel commented on Jan 13, 2026

    @tobias-haenel
    Author

    I had no interest in it anymore. Sorry for the confusion.

  5. PeterCardenas commented on Jan 14, 2026

    @PeterCardenas
    Contributor

    last i checked it doesn't work, i'll close the pr to avoid confusion

  6. sparr commented on Aug 6, 2026

    @sparr
    Contributor
    A = {}
    A.c = function() end
    A.c()

    I encountered this bug for a function assigned into a global table. VSCode Go To Definition on the c in A.c() produces two candidates, both on the previous line, one at c and the other at function() end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions