luamoduleneovimneovim-plugin

Difference between require("some.module") and require("some").module?


I currently have this (working) configuration in my neovim:

   {
        "nvim-telescope/telescope.nvim",
        dependencies = {
            ...
            "jvgrootveld/telescope-zoxide",
        },
        config = function()
            -- First setup telescope
            local telescope = require("telescope")
            local builtin = require("telescope.builtin")

            telescope.setup({
                ...
                extensions = {
                    zoxide = {
                        mappings = {
                            default = {
                                keepinsert = true,
                                action = function(selection)
                                    builtin.find_files({ cwd = selection.path }) -- the line in question
                                end,
                            },
                        },
                    },
                },
            })
            telescope.load_extension("zoxide")
        end,
        ...
    },

If I change the line in question to

telescope.builtin.find_files({ cwd = selection.path })

I get the following error:

E5108: Error executing lua: /home/sbeer/.config/nvim/lua/plugins/telescope.lua:38: attempt to index field 'builtin' (a nil value)
stack traceback:
        /home/sbeer/.config/nvim/lua/plugins/telescope.lua:38: in function 'action'
        ...lescope-zoxide/lua/telescope/_extensions/zoxide/list.lua:86: in function 'run_replace_or_original'
        ...re/nvim/lazy/telescope.nvim/lua/telescope/actions/mt.lua:65: in function 'key_func'
        ...hare/nvim/lazy/telescope.nvim/lua/telescope/mappings.lua:253: in function <...hare/nvim/lazy/telescope.nvim/lua/telescope/mappings.lua:252>

What exactly is the difference between the two?


Solution

  • As pointed out in this answer,

    It is up to the designer of the module to decide what can be seen from the outside.

    The working require("telescope.builtin") approach loads a separate module that contains the find_files function. However, that module, and so its function, apparently is not automatically part of the telescope table/module. Since the builtin module is not accessible from the outside by default, Lua in this instance assumes that builtin in the reference telescope.builtin is a field of the telescope module, which it isn't, resulting in the error "attempt to index **field** 'builtin' (a nil value)."

    Now, if you really want to use the require("some").module approach, you may add a line to expose builtin as a field of telescope explicitly, like so:

    local telescope = require("telescope")
    telescope.builtin = require("telescope.builtin") -- Add this line
    

    Then the line in question would work correctly:

    telescope.builtin.find_files({ cwd = selection.path })
    

    But to avoid ambiguity and unnecessary additions to the telescope namespace, I'd say it’s better to stick with the working version:

    local builtin = require("telescope.builtin")
    ...
    builtin.find_files({ cwd = selection.path })
    

    Finally, I think it was a deliberate intention by the developers of the telescope module to have that structure, as their own code snippets show the require("some.module") approach:

    --- To use any of Telescope's default options or any picker-specific options, call your desired picker by passing a lua
    --- table to the picker with all of the options you want to use. Here's an example with the live_grep picker:
    ---
    --- <code>
    ---   :lua require('telescope.builtin').live_grep({
    ---     prompt_title = 'find string in open buffers...',
    ---     grep_open_files = true
    ---   })
    ---   -- or with dropdown theme
    ---   :lua require('telescope.builtin').find_files(require('telescope.themes').get_dropdown{
    ---     previewer = false
    ---   })
    --- </code>
    

    You can check such things by iterating over the module's fields (as it typically has the format of a simple table):

    local telescope = require("telescope")
    for key, value in pairs(telescope) do
        print(key, value)
    end
    

    This will list all keys (field names) and their corresponding values in the module.