cvigilv / esqueleto.nvim

Reduce your boilerplate code the lazy-bones way
MIT License
100 stars 7 forks source link

fix: error when entering new file whose name matches one of the patterns case insensitively #27

Open Futarimiti opened 1 year ago

Futarimiti commented 1 year ago

Hi, another issue found when testing.

Minimum reproducible example:

-- config
require('esqueleto').setup { patterns = { 'main.py' } }

Then, with nvim, create and edit file Main.py (or whatever filename as long as it insensitively matches 'main.py' e.g. MAIN.py, mAIn.PY...), will trigger the following error:

Error detected while processing BufWinEnter Autocommands for "main.py":
Error executing lua callback: ...l/share/nvim/lazy/esqueleto.nvim/lua/esqueleto/utils.lua:18: attempt to concatenate local 'pattern' (a
 nil value)
stack traceback:
        ...l/share/nvim/lazy/esqueleto.nvim/lua/esqueleto/utils.lua:18: in function 'gettemplates'
        ...l/share/nvim/lazy/esqueleto.nvim/lua/esqueleto/utils.lua:86: in function 'inserttemplate'
        ...share/nvim/lazy/esqueleto.nvim/lua/esqueleto/autocmd.lua:21: in function <...share/nvim/lazy/esqueleto.nvim/lua/esqueleto/au
tocmd.lua:18>

And after some debugging, I believe here's the reason:

  1. Patterns provided by users are directly passed as autocmd patterns:
      vim.api.nvim_create_autocmd(
        { "BufWinEnter", "BufReadPost", "FileType" },
        {
          group = group,
          desc = "esqueleto.nvim :: Insert template",
          pattern = opts.patterns,  -- { 'main.py' }
          callback = function()
            local filepath = vim.fn.expand("%")
            local emptyfile = vim.fn.getfsize(filepath) < 4
            if emptyfile then utils.inserttemplate(opts) end
          end
        }
      )

    Though not directly documented in the manual, it seems that autocmd patterns are indeed case-insensitive, therefore Main.py matches the main.py and the callback is executed.

  2. Then, within inserttemplate:

    M.inserttemplate = function(opts)
      ...
      -- Identify if pattern matches user configuration
      local pattern = nil
      if not _G.esqueleto_inserted[filepath] then
        -- match either filename or extension. Filename has priority
        if vim.tbl_contains(opts.patterns, filename) then
          pattern = filename
        elseif vim.tbl_contains(opts.patterns, filetype) then
          pattern = filetype
        end
    
        -- Get templates for selected pattern
        local templates = M.gettemplates(pattern, opts.directories)
        ...
      end
    end

    Where vim.tbl_contains, which is case-sensitive, is used to get that pattern. Of course, opts.patterns does not contain Main.py, and pattern, without nil-checks, will be passed to gettemplates to be concatenated with filepaths.